{"id":15775415,"url":"https://github.com/styd/sd_struct","last_synced_at":"2026-05-18T06:37:57.308Z","repository":{"id":98613145,"uuid":"87628928","full_name":"styd/sd_struct","owner":"styd","description":"Searchable Deep Struct","archived":false,"fork":false,"pushed_at":"2018-09-25T00:08:07.000Z","size":32,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-11T17:04:26.740Z","etag":null,"topics":["activesupport","data","gem","openstruct","rails","ruby","structure"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/styd.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-04-08T11:25:52.000Z","updated_at":"2018-09-25T00:03:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"ed3a4fa1-dba6-4ca9-b29e-660c4ce3f84e","html_url":"https://github.com/styd/sd_struct","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/styd%2Fsd_struct","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/styd%2Fsd_struct/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/styd%2Fsd_struct/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/styd%2Fsd_struct/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/styd","download_url":"https://codeload.github.com/styd/sd_struct/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246484272,"owners_count":20785046,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activesupport","data","gem","openstruct","rails","ruby","structure"],"created_at":"2024-10-04T17:00:49.111Z","updated_at":"2026-05-18T06:37:52.288Z","avatar_url":"https://github.com/styd.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SdStruct\n\nAnother alternative to OpenStruct that is searchable with xpath like syntax and\ngoes deeper in consuming the passed Hash and transforming it back to Hash or JSON.\n\n## Usage\n\n### From JSON\n\nExample of a response body in JSON.\n\n```JSON\n\"{\\\"object\\\":{\\\"a\\\":\\\"bau bau\\\",\\\"c\\\":\\\"boo boo\\\"},\\\"array\\\":[{\\\"one\\\":1,\\\"two\\\":2,\\\"three\\\":3}],\\\"two words\\\":\\\"Foo bar\\\"}\"\n```\n\n\u003e SdStruct inspects as OpenStruct would inspect.\n\n```ruby\n## with OpenStruct\nostruct = JSON.parse(json, object_class: OpenStruct)\n# =\u003e #\u003cOpenStruct object=#\u003cOpenStruct a=\"bau bau\", c=\"boo boo\"\u003e,\n# array=[#\u003cOpenStruct one=1, two=2, three=3\u003e], two words=\"Foo bar\"\u003e\n\nostruct[\"two words\"]\n# =\u003e \"Foo bar\"\n\n## with SdStruct\nsd_struct = JSON.parse(json, object_class: SdStruct)\n# =\u003e #\u003cSdStruct object=#\u003cSdStruct a=\"bau bau\", c=\"boo boo\"\u003e,\n# array=[#\u003cSdStruct one=1, two=2, three=3\u003e], two words=\"Foo bar\"\u003e\n\nsd_struct[\"two words\"]\n# =\u003e \"Foo bar\"\n\n# By the way, you can also use `send` method to access spaced key/field\no_struct.send(\"two words\")\nsd_struct.send(\"two words\")\n# =\u003e \"Foo bar\"\n```\n\n\n\u003e OpenStruct's `to_h` doesn't return a hash deeply.\n\n```ruby\n## OpenStruct\no_struct.to_h\n# =\u003e {:object=\u003e#\u003cOpenStruct a=\"bau bau\", c=\"boo boo\"\u003e,\n# :array=\u003e[#\u003cOpenStruct one=1, two=2, three=3\u003e], :\"two words\"=\u003e\"Foo bar\"}\n\n## SdStruct\nsd_struct.to_h\n# =\u003e {:object=\u003e{:a=\u003e\"bau bau\", :c=\u003e\"boo boo\"},\n# :array=\u003e[{:one=\u003e1, :two=\u003e2, :three=\u003e3}], \"two words\"=\u003e\"Foo bar\"}\n```\n\n\n\u003e OpenStruct doesn't have search or deep digging functionalities\n\n```ruby\nsd_struct.find('object/a')\n# =\u003e \"bau bau\"\n\nsd_struct.find('/array/0/one')\n# =\u003e 1\n\nsd_struct.find('object-\u003ea', separator: '-\u003e')\n# =\u003e \"bau bau\"\n\n# You can push it to find deeper. It will return the first occurrence of the matched field\nsd_struct.find('.array..one', separator: '.')\n# =\u003e 1\n\nsd_struct.find('//a')\n# =\u003e \"bau bau\"\n\nsd_struct.find('//0/one')\n# =\u003e 1\n\nsd_struct.find('//one')\n# =\u003e 1\n\nsd_struct.find('//four')\n# =\u003e nil\n\nsd_struct.dig_deep(0, :one)\n# =\u003e 1\n\nsd_struct.dig_deep(:one)\n# =\u003e 1\n```\n\n\n### From Hash\n\nExample of a Hash.\n\n```ruby\n{\n  :object =\u003e {\n    :a =\u003e \"bau bau\",\n    :c =\u003e \"boo boo\"\n  },\n  :array =\u003e [\n    {\n      :one =\u003e 1,\n      :two =\u003e 2,\n      :three =\u003e 3\n    }\n  ],\n  \"two words\" =\u003e \"Foo bar\"\n}\n```\n\n\n\u003e OpensStruct doesn't consume a hash deeply\n\n```ruby\n## with OpenStruct\no_struct = OpenStruct.new(hash)\n# =\u003e #\u003cOpenStruct object={:a=\u003e\"bau bau\", :c=\u003e\"boo boo\"},\n# array=[{:one=\u003e1, :two=\u003e2, :three=\u003e3}], two words=\"Foo bar\"\u003e\n\n## with SdStruct\nsd_struct = SdStruct.new(hash)\n# =\u003e #\u003cSdStruct .object=#\u003cSdStruct .a=\"bau bau\", .c=\"boo boo\"\u003e,\n# .array=[#\u003cSdStruct .one=1, .two=2, .three=3\u003e], ['two words']=\"Foo bar\"\u003e\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install activesupport\n\nIn your code:\n\n```ruby\nrequire 'active_support/all'\n```\n\n\n## Benchmark against OpenStruct and its other alternatives\n\n### Initialization\n```sh\n$ ruby bench/initialization.rb\n\nInitialization\nWarming up --------------------------------------\n             ostruct    23.719k i/100ms\nfiner_struct/mutable    21.343k i/100ms\nfiner_struct/immutable\n                        20.148k i/100ms\n            hashugar    17.404k i/100ms\n         hashie/mash     8.790k i/100ms\n               hashr     5.875k i/100ms\n           sd_struct    13.516k i/100ms\nCalculating -------------------------------------\n             ostruct    355.611k (±15.1%) i/s -      1.731M in   5.045464s\nfiner_struct/mutable    285.687k (±15.2%) i/s -      1.387M in   5.020978s\nfiner_struct/immutable\n                        311.364k (± 6.6%) i/s -      1.551M in   5.011959s\n            hashugar    237.504k (± 2.7%) i/s -      1.201M in   5.060197s\n         hashie/mash    110.607k (±17.4%) i/s -    527.400k in   5.003895s\n               hashr     78.312k (± 9.7%) i/s -    387.750k in   5.004247s\n           sd_struct    201.086k (±13.7%) i/s -    986.668k in   5.015946s\n\nComparison:\n             ostruct:   355611.1 i/s\nfiner_struct/immutable:   311364.4 i/s - same-ish: difference falls within error\nfiner_struct/mutable:   285686.7 i/s - same-ish: difference falls within error\n            hashugar:   237503.8 i/s - 1.50x  slower\n           sd_struct:   201085.5 i/s - 1.77x  slower\n         hashie/mash:   110606.6 i/s - 3.22x  slower\n               hashr:    78311.9 i/s - 4.54x  slower\n```\n\n### Getter\n```sh\n$ ruby bench/getter.rb\n\nGetter\nWarming up --------------------------------------\n             ostruct    62.616k i/100ms\n         hashie/mash    32.544k i/100ms\n               hashr    39.530k i/100ms\n            hashugar    47.171k i/100ms\n           sd_struct    64.453k i/100ms\nCalculating -------------------------------------\n             ostruct      3.807M (± 4.0%) i/s -     19.035M in   5.009086s\n         hashie/mash    731.653k (± 4.2%) i/s -      3.677M in   5.035053s\n               hashr      1.081M (± 3.3%) i/s -      5.416M in   5.014805s\n            hashugar      1.631M (± 1.9%) i/s -      8.161M in   5.006753s\n           sd_struct      3.969M (± 2.3%) i/s -     19.852M in   5.005272s\n\nComparison:\n           sd_struct:  3968540.0 i/s\n             ostruct:  3806506.9 i/s - same-ish: difference falls within error\n            hashugar:  1630525.2 i/s - 2.43x  slower\n               hashr:  1081108.5 i/s - 3.67x  slower\n         hashie/mash:   731653.2 i/s - 5.42x  slower\n```\n\n### Setter\n```sh\n$ ruby bench/setter.rb\n\nSetter\nWarming up --------------------------------------\n             ostruct    48.906k i/100ms\n         hashie/mash    10.925k i/100ms\n               hashr    26.087k i/100ms\n            hashugar    37.424k i/100ms\n           sd_struct    50.481k i/100ms\nCalculating -------------------------------------\n             ostruct      1.993M (± 5.5%) i/s -      9.977M in   5.022685s\n         hashie/mash    149.320k (± 1.9%) i/s -    753.825k in   5.050346s\n               hashr    475.269k (± 2.0%) i/s -      2.400M in   5.051718s\n            hashugar      1.001M (± 3.2%) i/s -      5.015M in   5.015471s\n           sd_struct      1.921M (± 4.8%) i/s -      9.591M in   5.005693s\n\nComparison:\n             ostruct:  1992500.5 i/s\n           sd_struct:  1921339.3 i/s - same-ish: difference falls within error\n            hashugar:  1000912.9 i/s - 1.99x  slower\n               hashr:   475268.8 i/s - 4.19x  slower\n         hashie/mash:   149319.7 i/s - 13.34x  slower\n```\n\n### Deep Initialization\n```sh\n$ ruby bench/deep_initialization.rb\n\nDeep Initialization\nWarming up --------------------------------------\n            hashugar    10.180k i/100ms\n         hashie/mash     4.863k i/100ms\n               hashr     3.111k i/100ms\n           sd_struct     9.024k i/100ms\nCalculating -------------------------------------\n            hashugar    135.930k (± 2.5%) i/s -    682.060k in   5.021100s\n         hashie/mash     57.260k (± 1.3%) i/s -    286.917k in   5.011621s\n               hashr     36.165k (± 7.7%) i/s -    180.438k in   5.032553s\n           sd_struct    111.588k (± 6.4%) i/s -    559.488k in   5.046688s\n\nComparison:\n            hashugar:   135929.6 i/s\n           sd_struct:   111588.5 i/s - 1.22x  slower\n         hashie/mash:    57259.7 i/s - 2.37x  slower\n               hashr:    36164.5 i/s - 3.76x  slower\n```\n\n### Deep Getter\n```sh\n$ ruby bench/deep_getter.rb\n\nDeep Getter\nWarming up --------------------------------------\n         hashie/mash    21.798k i/100ms\n               hashr    26.628k i/100ms\n            hashugar    34.012k i/100ms\n           sd_struct    53.437k i/100ms\nCalculating -------------------------------------\n         hashie/mash    394.113k (± 4.5%) i/s -      1.984M in   5.044513s\n               hashr    551.904k (± 1.9%) i/s -      2.769M in   5.019679s\n            hashugar    862.010k (± 1.4%) i/s -      4.320M in   5.011998s\n           sd_struct      2.110M (± 8.6%) i/s -     10.420M in   5.016246s\n\nComparison:\n           sd_struct:  2110253.6 i/s\n            hashugar:   862010.3 i/s - 2.45x  slower\n               hashr:   551903.9 i/s - 3.82x  slower\n         hashie/mash:   394112.6 i/s - 5.35x  slower\n```\n\n### Deep Setter\n```sh\n$ ruby bench/deep_setter.rb\n\nDeep Setter\nWarming up --------------------------------------\n         hashie/mash     5.145k i/100ms\n               hashr     5.378k i/100ms\n            hashugar    25.964k i/100ms\n           sd_struct    13.419k i/100ms\nCalculating -------------------------------------\n         hashie/mash     61.130k (± 3.3%) i/s -    308.700k in   5.056208s\n               hashr     62.056k (± 1.4%) i/s -    311.924k in   5.027385s\n            hashugar    516.481k (± 1.9%) i/s -      2.596M in   5.028998s\n           sd_struct    195.273k (± 2.1%) i/s -    979.587k in   5.018694s\n\nComparison:\n            hashugar:   516480.7 i/s\n           sd_struct:   195272.8 i/s - 2.64x  slower\n               hashr:    62056.5 i/s - 8.32x  slower\n         hashie/mash:    61130.5 i/s - 8.45x  slower\n```\n\n### \\#to_h\n```sh\n$ ruby bench/to_h.rb\n\n\\#to_h\nOriginal Hash: {\"a\"=\u003e\"a\", :b=\u003e\"b\"}\nOpenStruct: {:a=\u003e\"a\", :b=\u003e\"b\"}\nHashie::Mash: {\"a\"=\u003e\"a\", \"b\"=\u003e\"b\"}\nHashr: {:a=\u003e\"a\", :b=\u003e\"b\"}\nHashugar: {\"a\"=\u003e\"a\", :b=\u003e\"b\"}\nSdStruct: {\"a\"=\u003e\"a\", :b=\u003e\"b\"}\nWarming up --------------------------------------\n             ostruct    29.118k i/100ms\n         hashie/mash    46.945k i/100ms\n               hashr    10.818k i/100ms\n            hashugar    49.588k i/100ms\n           sd_struct    48.183k i/100ms\nCalculating -------------------------------------\n             ostruct    609.813k (± 2.4%) i/s -      3.057M in   5.016669s\n         hashie/mash      1.378M (± 1.8%) i/s -      6.901M in   5.009253s\n               hashr    136.990k (± 2.0%) i/s -    692.352k in   5.056100s\n            hashugar      1.630M (± 2.9%) i/s -      8.182M in   5.024282s\n           sd_struct      1.609M (± 6.0%) i/s -      8.047M in   5.020611s\n\nComparison:\n            hashugar:  1629886.1 i/s\n           sd_struct:  1609137.2 i/s - same-ish: difference falls within error\n         hashie/mash:  1378083.4 i/s - 1.18x  slower\n             ostruct:   609812.7 i/s - 2.67x  slower\n               hashr:   136990.4 i/s - 11.90x  slower\n```\n\n### \\#to_json\n```sh\n$ ruby bench/to_json.rb\n\n\\#to_json\nOpenStruct: \"#\u003cOpenStruct a=\\\"a\\\", b=\\\"b\\\"\u003e\"\nHashie::Mash: {\"a\":\"a\",\"b\":\"b\"}\nHashr:\nHashugar: \"#\u003cHashugar:0x00007fffdfbead98\u003e\"\nSdStruct: {\"a\":\"a\",\"b\":\"b\"}\nWarming up --------------------------------------\n         hashie/mash     6.444k i/100ms\n           sd_struct    10.834k i/100ms\nCalculating -------------------------------------\n         hashie/mash     77.011k (± 1.7%) i/s -    386.640k in   5.021927s\n           sd_struct    143.077k (± 2.0%) i/s -    715.044k in   4.999559s\n\nComparison:\n           sd_struct:   143076.8 i/s\n         hashie/mash:    77011.2 i/s - 1.86x  slower\n``\n\n### Deep \\#to_h\n```sh\n$ ruby bench/deep_to_h.rb\n\nDeep \\#to_h\nHashie::Mash: {\"a\"=\u003e#\u003cHashie::Mash b=\"b\"\u003e}\nHashr: {:a=\u003e{:b=\u003e\"b\"}}\nHashugar: {\"a\"=\u003e{:b=\u003e\"b\"}}\nSdStruct: {\"a\"=\u003e{:b=\u003e\"b\"}}\nWarming up --------------------------------------\n         hashie/mash    42.458k i/100ms\n               hashr     9.632k i/100ms\n            hashugar    49.011k i/100ms\n           sd_struct    48.960k i/100ms\nCalculating -------------------------------------\n         hashie/mash      1.297M (± 4.6%) i/s -      6.496M in   5.020400s\n               hashr    124.548k (± 1.5%) i/s -    626.080k in   5.028001s\n            hashugar      1.947M (± 3.4%) i/s -      9.753M in   5.015420s\n           sd_struct      1.894M (± 3.4%) i/s -      9.498M in   5.022113s\n\nComparison:\n            hashugar:  1947158.3 i/s\n           sd_struct:  1893594.3 i/s - same-ish: difference falls within error\n         hashie/mash:  1297445.4 i/s - 1.50x  slower\n               hashr:   124547.7 i/s - 15.63x  slower\n```\n\n### Deep \\#to_json\n```sh\n$ ruby bench/deep_to_json.rb\n\nDeep \\#to_json\nHashie::Mash: {\"a\":{\"b\":\"b\"}}\nHashr:\nHashugar: \"#\u003cHashugar:0x00007fffb95885c0\u003e\"\nSdStruct: {\"a\":{\"b\":\"b\"}}\nWarming up --------------------------------------\n         hashie/mash     5.796k i/100ms\n           sd_struct     9.692k i/100ms\nCalculating -------------------------------------\n         hashie/mash     62.437k (±16.3%) i/s -    301.392k in   5.043041s\n           sd_struct    120.555k (±18.9%) i/s -    581.520k in   5.053842s\n\nComparison:\n           sd_struct:   120555.1 i/s\n         hashie/mash:    62437.1 i/s - 1.93x  slower\n```\n\n### Object Class\n```sh\n$ ruby bench/object_class.rb\n\nJSON#parse :object_class\nOpenStruct: #\u003cOpenStruct object=#\u003cOpenStruct a=\"bau bau\", c=\"boo boo\"\u003e, array=[#\u003cOpenStruct one=1, two=2, three=3\u003e], two words=\"Foo bar\"\u003e\nHashie::Mash: #\u003cHashie::Mash array=#\u003cHashie::Array [#\u003cHashie::Mash one=1 three=3 two=2\u003e]\u003e object=#\u003cHashie::Mash a=\"bau bau\" c=\"boo boo\"\u003e two words=\"Foo bar\"\u003e\nSdStruct: #\u003cSdStruct object=#\u003cSdStruct a=\"bau bau\", c=\"boo boo\"\u003e, array=[#\u003cSdStruct one=1, two=2, three=3\u003e], two words=\"Foo bar\"\u003e\nWarming up --------------------------------------\n             ostruct   915.000  i/100ms\n         hashie/mash     1.079k i/100ms\n           sd_struct     1.045k i/100ms\nCalculating -------------------------------------\n             ostruct     11.261k (± 5.5%) i/s -     56.730k in   5.053413s\n         hashie/mash     11.139k (± 1.4%) i/s -     56.108k in   5.037903s\n           sd_struct     10.584k (± 4.6%) i/s -     53.295k in   5.045103s\n\nComparison:\n             ostruct:    11261.4 i/s\n         hashie/mash:    11139.5 i/s - same-ish: difference falls within error\n           sd_struct:    10584.3 i/s - same-ish: difference falls within error\n```\n\n### SdStruct Getters\n```sh\n$ ruby bench/sd_struct_getters.rb\n\nSdStruct Getters\nWarming up --------------------------------------\n        method chain    48.824k i/100ms\n      xpath absolute     1.927k i/100ms\n      xpath relative     1.394k i/100ms\nCalculating -------------------------------------\n        method chain      1.660M (± 3.2%) i/s -      8.300M in   5.004975s\n      xpath absolute     20.278k (± 1.7%) i/s -    102.131k in   5.038094s\n      xpath relative     14.291k (± 1.7%) i/s -     72.488k in   5.073917s\n\nComparison:\n        method chain:  1660214.5 i/s\n      xpath absolute:    20277.9 i/s - 81.87x  slower\n      xpath relative:    14290.7 i/s - 116.17x  slower\n```\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'sd_struct'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install sd_struct\n\n## Attribution\n\nHashugar and OpenStruct are some of the primary sources of inspiration for this project.\nIt closely follows the way Hashugar takes nested hash input and the way OpenStruct\ncreates method on the first call.\n\n## TODO\n\n  * Add charts and tables for the benchmarks.\n\n\n## Contributing\n\n1. Fork it ( https://github.com/styd/sd_struct/fork )\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstyd%2Fsd_struct","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstyd%2Fsd_struct","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstyd%2Fsd_struct/lists"}