{"id":26733477,"url":"https://github.com/amcaplan/dynamic_class","last_synced_at":"2025-04-14T12:41:31.682Z","repository":{"id":62557603,"uuid":"50791830","full_name":"amcaplan/dynamic_class","owner":"amcaplan","description":"Create classes that define themselves... eventually.","archived":false,"fork":false,"pushed_at":"2016-07-04T10:40:25.000Z","size":26,"stargazers_count":19,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-28T01:52:32.593Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/amcaplan.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2016-01-31T19:46:31.000Z","updated_at":"2022-03-21T19:38:22.000Z","dependencies_parsed_at":"2022-11-03T06:45:30.235Z","dependency_job_id":null,"html_url":"https://github.com/amcaplan/dynamic_class","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amcaplan%2Fdynamic_class","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amcaplan%2Fdynamic_class/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amcaplan%2Fdynamic_class/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amcaplan%2Fdynamic_class/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amcaplan","download_url":"https://codeload.github.com/amcaplan/dynamic_class/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248883079,"owners_count":21177147,"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":[],"created_at":"2025-03-28T01:49:51.343Z","updated_at":"2025-04-14T12:41:31.644Z","avatar_url":"https://github.com/amcaplan.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Gem Version](https://badge.fury.io/rb/dynamic_class.svg)](https://badge.fury.io/rb/dynamic_class)\n[![Build Status](https://travis-ci.org/amcaplan/dynamic_class.svg?branch=master)](https://travis-ci.org/amcaplan/dynamic_class)\n[![Code Climate](https://codeclimate.com/github/amcaplan/dynamic_class.png)](https://codeclimate.com/github/amcaplan/dynamic_class)\n\n# DynamicClass\n\nMany developers use `OpenStruct` as a convenient way of consuming APIs through\na nifty data object. But the performance penalty is pretty awful.\n\n`DynamicClass` offers a better solution, optimizing for the case where you\nneed to create objects with the same set of properties every time, but you\ncan't define the needed keys until runtime. `DynamicClass` works by defining\ninstance methods on the class every time it encounters a new propery.\n\nLet's see it in action:\n\n``` ruby\nAnimal = DynamicClass.new do\n  def speak\n    \"The #{type} makes a #{sound} sound!\"\n  end\nend\n\ndog = Animal.new(type: 'dog', sound: 'woof')\n# =\u003e #\u003cAnimal:0x007fdb2b818ba8 @type=\"dog\", @sound=\"woof\"\u003e\ndog.speak\n# =\u003e The dog makes a woof sound!\ndog.ears = 'droopy'\ndog[:nose] = ['cold', 'wet']\ndog['tail'] = 'waggable'\ndog\n# =\u003e #\u003cAnimal:0x007fc26b1841d0 @type=\"dog\", @sound=\"woof\", @ears=\"droopy\", @nose=[\"cold\", \"wet\"], @tail=\"waggable\"\u003e\ndog.type\n# =\u003e \"dog\"\ndog.tail\n# =\u003e \"waggable\"\n\ncat = Animal.new\n# =\u003e #\u003cAnimal:0x007fdb2b83b180\u003e\ncat.to_h\n# =\u003e {:type=\u003enil, :sound=\u003enil, :ears=\u003enil, :nose=\u003enil, :tail=\u003enil}\n# The class has been changed by the dog!\n```\n\nBecause methods are defined on the class (unlike `OpenStruct` which defines\nmethods on the object's singleton class), there is no need to define a method\nmore than once. This means that, past the first time a property is added,\nthe cost of setting a property drops.\n\nThe results are pretty astounding. Here are the results of the benchmark in\n`bin/benchmark.rb` (including a few other `OpenStruct`-like solutions for\ncomparison), run on Ruby 2.3.1; the final benchmark is most representative of\nthe average case:\n\n```\nInitialization benchmark\n\nWarming up --------------------------------------\n          OpenStruct    84.801k i/100ms\nPersistentOpenStruct    74.901k i/100ms\n      OpenFastStruct    81.303k i/100ms\n        DynamicClass    97.024k i/100ms\n        RegularClass   211.767k i/100ms\nCalculating -------------------------------------\n          OpenStruct      1.104M (± 5.8%) i/s -      5.512M in   5.011886s\nPersistentOpenStruct    941.181k (± 5.2%) i/s -      4.719M in   5.027485s\n      OpenFastStruct      1.020M (± 6.0%) i/s -      5.122M in   5.040500s\n        DynamicClass      1.309M (± 4.0%) i/s -      6.598M in   5.049905s\n        RegularClass      4.170M (± 4.0%) i/s -     20.965M in   5.036315s\n\nComparison:\n        RegularClass:  4169602.6 i/s\n        DynamicClass:  1308644.3 i/s - 3.19x slower\n          OpenStruct:  1103594.3 i/s - 3.78x slower\n      OpenFastStruct:  1019939.5 i/s - 4.09x slower\nPersistentOpenStruct:   941180.6 i/s - 4.43x slower\n\n\n\nAssignment Benchmark\n\nWarming up --------------------------------------\n          OpenStruct   216.147k i/100ms\nPersistentOpenStruct   210.657k i/100ms\n      OpenFastStruct   101.072k i/100ms\n        DynamicClass   311.870k i/100ms\n        RegularClass   312.066k i/100ms\nCalculating -------------------------------------\n          OpenStruct      4.505M (± 5.0%) i/s -     22.479M in   5.003206s\nPersistentOpenStruct      4.515M (± 5.0%) i/s -     22.540M in   5.005895s\n      OpenFastStruct      1.383M (± 3.5%) i/s -      6.974M in   5.048792s\n        DynamicClass     11.138M (± 5.0%) i/s -     55.825M in   5.026293s\n        RegularClass     11.069M (± 5.8%) i/s -     55.236M in   5.009156s\n\nComparison:\n        DynamicClass: 11137717.4 i/s\n        RegularClass: 11068826.7 i/s - same-ish: difference falls within error\nPersistentOpenStruct:  4514966.3 i/s - 2.47x slower\n          OpenStruct:  4505071.4 i/s - 2.47x slower\n      OpenFastStruct:  1383122.4 i/s - 8.05x slower\n\n\n\nAccess Benchmark\n\nWarming up --------------------------------------\n          OpenStruct   259.543k i/100ms\nPersistentOpenStruct   255.894k i/100ms\n      OpenFastStruct   225.799k i/100ms\n        DynamicClass   313.455k i/100ms\n        RegularClass   313.982k i/100ms\nCalculating -------------------------------------\n          OpenStruct      6.744M (± 5.0%) i/s -     33.741M in   5.016060s\nPersistentOpenStruct      6.863M (± 5.2%) i/s -     34.290M in   5.011129s\n      OpenFastStruct      4.717M (± 4.5%) i/s -     23.709M in   5.036478s\n        DynamicClass     11.467M (± 5.9%) i/s -     57.362M in   5.021761s\n        RegularClass     11.395M (± 6.6%) i/s -     56.831M in   5.011823s\n\nComparison:\n        DynamicClass: 11467320.5 i/s\n        RegularClass: 11395421.4 i/s - same-ish: difference falls within error\nPersistentOpenStruct:  6862609.3 i/s - 1.67x slower\n          OpenStruct:  6744325.9 i/s - 1.70x slower\n      OpenFastStruct:  4717334.0 i/s - 2.43x slower\n\n\n\nAll-Together Benchmark\n\nWarming up --------------------------------------\n          OpenStruct    13.929k i/100ms\nPersistentOpenStruct    64.546k i/100ms\n      OpenFastStruct    45.014k i/100ms\n        DynamicClass    96.783k i/100ms\n        RegularClass   197.149k i/100ms\nCalculating -------------------------------------\n          OpenStruct    147.361k (± 4.8%) i/s -    738.237k in   5.021813s\nPersistentOpenStruct    766.793k (± 5.8%) i/s -      3.873M in   5.069128s\n      OpenFastStruct    525.565k (± 4.1%) i/s -      2.656M in   5.062072s\n        DynamicClass      1.251M (± 4.0%) i/s -      6.291M in   5.038697s\n        RegularClass      3.758M (± 4.1%) i/s -     18.926M in   5.046044s\n\nComparison:\n        RegularClass:  3757567.8 i/s\n        DynamicClass:  1250634.2 i/s - 3.00x slower\nPersistentOpenStruct:   766792.7 i/s - 4.90x slower\n      OpenFastStruct:   525565.1 i/s - 7.15x slower\n          OpenStruct:   147361.4 i/s - 25.50x slower\n```\n\n`DynamicClass` is still behind plain old Ruby classes, but it's the best out of\nthe pack when it comes to `OpenStruct` and friends.\n\n## WARNING!\n\nThis class should only be used to consume trusted APIs, or for similar purposes.\nIt should never be used to take in user input. This will open you up to a memory\nleak DoS attack, since every new key becomes a new method defined on the class,\nand is never erased.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'dynamic_class'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install dynamic_class\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run\n`rake spec` to run the tests. You can run the benchmark using `rake benchmark`.\nYou can also run `bin/console` for an interactive prompt that will allow you to\nexperiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`.\n\n## Contributing\n\nBug reports and pull requests are welcome. This project is intended to be a\nsafe, welcoming space for collaboration, and contributors are expected to adhere\nto the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\nFor functionality changes or bug fixes, please include tests. For performance\nenhancements, please run the benchmarks and include results in your pull\nrequest.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famcaplan%2Fdynamic_class","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famcaplan%2Fdynamic_class","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famcaplan%2Fdynamic_class/lists"}