{"id":13878955,"url":"https://github.com/jasl/activeentity","last_synced_at":"2025-03-16T16:30:31.409Z","repository":{"id":39699458,"uuid":"163760136","full_name":"jasl/activeentity","owner":"jasl","description":"Active Record without Database","archived":false,"fork":false,"pushed_at":"2023-03-09T01:38:14.000Z","size":312,"stargazers_count":52,"open_issues_count":7,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-12T03:53:08.368Z","etag":null,"topics":["activemodel","activerecord","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jasl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-01-01T19:10:58.000Z","updated_at":"2024-01-04T21:56:53.000Z","dependencies_parsed_at":"2024-01-13T20:56:22.862Z","dependency_job_id":"dfe74ad1-7440-4bd6-ba8f-5a73f462ddad","html_url":"https://github.com/jasl/activeentity","commit_stats":{"total_commits":66,"total_committers":3,"mean_commits":22.0,"dds":"0.10606060606060608","last_synced_commit":"bfad638462330b02ca5d5912f7968f5803a90e6e"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasl%2Factiveentity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasl%2Factiveentity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasl%2Factiveentity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasl%2Factiveentity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jasl","download_url":"https://codeload.github.com/jasl/activeentity/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221665766,"owners_count":16860310,"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":["activemodel","activerecord","rails","ruby"],"created_at":"2024-08-06T08:02:05.276Z","updated_at":"2024-10-27T10:53:46.251Z","avatar_url":"https://github.com/jasl.png","language":"Ruby","readme":"Active Entity\n====\n\nActive Entity is a Rails virtual model solution based on ActiveModel and it's design for Rails 6+.\n\nActive Entity is forked from Active Record by removing all database relates codes, so it nearly no need to learn how to use.\n\n## About Virtual Model\n\nVirtual Model is the model not backed by a database table, usually used as \"form model\" or \"presenter\", because it's implement interfaces of Active Model, so you can use it like a normal Active Record model in your Rails app.\n\n## Features\n\n### Attribute declaration\n\n```ruby\nclass Book \u003c ActiveEntity::Base\n  attribute :title, :string\n  attribute :tags, :string, array: true, default: []\nend\n```\n\nSame usage with Active Record, [Learn more](https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute).\n\nOne enhancement is `array: true` that transform the attribute to an array that can accept multiple values.\n\n### Nested attributes\n\nActive Entity supports its own variant of nested attributes via the `embeds_one` / `embeds_many` macros. The intention is to be mostly compatible with ActiveRecord's `accepts_nested_attributes_for` functionality.\n\n```ruby\nclass Holiday \u003c ActiveEntity::Base\n  attribute :date, :date\n  validates :date, presence: true\nend\n\nclass HolidaysForm \u003c ActiveEntity::Base\n  embeds_many :holidays\n  accepts_nested_attributes_for :holidays, reject_if: :all_blank\nend\n```\n\n### Validations\n\n```ruby\nclass Book \u003c ActiveEntity::Base\n  attribute :title, :string\n  validates :title, presence: true\nend\n```\n\nSupported Active Record validations:\n\n- [acceptance](https://guides.rubyonrails.org/active_record_validations.html#acceptance)\n- [confirmation](https://guides.rubyonrails.org/active_record_validations.html#confirmation)\n- [exclusion](https://guides.rubyonrails.org/active_record_validations.html#exclusion)\n- [format](https://guides.rubyonrails.org/active_record_validations.html#format)\n- [inclusion](https://guides.rubyonrails.org/active_record_validations.html#inclusion)\n- [length](https://guides.rubyonrails.org/active_record_validations.html#length)\n- [numericality](https://guides.rubyonrails.org/active_record_validations.html#numericality)\n- [presence](https://guides.rubyonrails.org/active_record_validations.html#presence)\n- [absence](https://guides.rubyonrails.org/active_record_validations.html#absence)\n\n[Common validation options](https://guides.rubyonrails.org/active_record_validations.html#common-validation-options) supported too.\n\n#### `subset` validation\n\nBecause Active Entity supports array attribute, for some reason, you may want to test values of an array attribute are all included in a given set.\n\nActive Entity provides `subset` validation to achieve that, it usage similar to `inclusion` or `exclusion`\n\n```ruby\nclass Steak \u003c ActiveEntity::Base\n  attribute :side_dishes, :string, array: true, default: []\n  validates :side_dishes, subset: { in: %w(chips mashed_potato salad) }\nend\n```\n\n#### `uniqueness_in_embeds` validation\n\nActive Entity provides `uniqueness_in_embeds` validation to test duplicate nesting virtual record.\n\nArgument `key` is attribute name of nested model, it also supports multiple attributes by given an array.\n\n```ruby\nclass Category \u003c ActiveEntity::Base\n  attribute :name, :string\nend\n\nclass Reviewer \u003c ActiveEntity::Base\n  attribute :first_name, :string\n  attribute :last_name, :string\nend\n\nclass Book \u003c ActiveEntity::Base\n  embeds_many :categories, index_errors: true\n  validates :categories, uniqueness_in_embeds: {key: :name}\n\n  embeds_many :reviewers\n  validates :reviewers, uniqueness_in_embeds: {key: [:first_name, :last_name]}\nend\n```\n\n#### `uniqueness_in_active_record` validation\n\nActive Entity provides `uniqueness_in_active_record` validation to test given `scope` doesn't present in ActiveRecord model.\n\nThe usage same as [uniqueness](https://guides.rubyonrails.org/active_record_validations.html#uniqueness) in addition you must give a AR model `class_name`\n\n```ruby\nclass Candidate \u003c ActiveEntity::Base\n  attribute :name, :string\n\n  validates :name,\n            uniqueness_on_active_record: {\n              class_name: \"Staff\"\n            }\nend\n```\n\n### Others\n\nThese Active Record feature also available in Active Entity\n\n- [`composed_of`](https://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html)\n- [`serializable_hash`](https://api.rubyonrails.org/classes/ActiveModel/Serialization.html#method-i-serializable_hash)\n- [`serialize`](https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize)\n- [`store`](https://api.rubyonrails.org/classes/ActiveRecord/Store.html)\n\n#### I18n\n\nSame to [Active Record I18n](https://guides.rubyonrails.org/i18n.html#translations-for-active-record-models), the only different is the root of locale YAML is `active_entity` instead of `activerecord`\n\n#### Enum\n\nYou can use the `enum` class method to define a set of possible values for an attribute. It is similar to the `enum` functionality in Active Model, but has significant enough quirks that you should think of them as distinct.\n\n```rb\nclass Example \u003c ActiveEntity::Base\n  attribute :steve, :integer\n  enum steve: [:martin, :carell, :buscemi]\nend\n\nexample = Example.new\nexample.attributes # =\u003e {\"steve\"=\u003enil}\nexample.steve = :carell\nexample.carell? # =\u003e true\nexample.attributes # =\u003e {\"steve\"=\u003e\"carell\"}\nexample.steve = 2\nexample.attributes # =\u003e {\"steve\"=\u003e\"buscemi\"}\n\n# IMPORTANT: the next line will only work if you implement an update! method\nexample.martin! # =\u003e {\"steve\"=\u003e\"martin\"}\n\nexample.steve = :bannon # ArgumentError ('bannon' is not a valid steve)\n```\n\nThe first thing you'll notice about the `:steve` attribute is that it is an \"Integer\", even though it might seem logical to define it as a String... TL;DR: don't do this. Internally enum tracks the possible values based on their index position in the array.\n\nIt's also possible to provide a Hash of possible values:\n\n```rb\nclass Example \u003c ActiveEntity::Base\n  attribute :steve, :integer, default: 9\n  enum steve: {martin: 5, carell: 12, buscemi: 9}\nend\n\nexample = Example.new\nexample.attributes # =\u003e {\"steve\"=\u003e\"buscemi\"}\n```\n\nThe other quirk of this implementation is that you must create your attribute before you call enum.\nenum does not create the search scopes that might be familar to Active Model users, since there is no search or where concept in Active Entity. You can, however, access the mapping directly to obtain the index number for a given value:\n\n```rb\nExample.steves[:buscemi] # =\u003e 9\n```\n\nYou can define prefixes and suffixes for your `enum` attributes. Note the underscores:\n\n```rb\nclass Conversation \u003c ActiveEntity::Base\n  attribute :status, :integer\n  attribute :comments_status, :integer\n  enum status: [ :active, :archived ], _suffix: true\n  enum comments_status: [ :active, :inactive ], _prefix: :comments\nend\n\nconversation = Conversation.new\nconversation.active_status! # only if you have an update! method\nconversation.archived_status? # =\u003e false\n\nconversation.comments_inactive! # only if you have an update! method\nconversation.comments_active? # =\u003e false\n```\n\n#### Read-only attributes\n\nYou can use `attr_readonly :title, :author` to prevent assign value to attribute after initialized.\n\nYou can use `enable_readonly!` and `disable_readonly!` to control the behavior.\n\n**Important: It's no effect with embeds or array attributes !!!**\n\n## Extending\n\nMost of Active Model plugins are compatible with Active Entity.\n\nYou need to include them manually.\n\nTested extensions:\n\n- [adzap/validates_timeliness](https://github.com/adzap/validates_timeliness)\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'activeentity', require: \"active_entity/railtie\"\n```\n\nAnd then execute:\n```bash\n$ bundle\n```\n\nOr install it yourself as:\n```bash\n$ gem install activeentity\n```\n\n## Other awesome gems\n\n- [makandra/active_type](https://github.com/makandra/active_type)\n\n## Contributing\n\n- Fork the project.\n- Make your feature addition or bug fix.\n- Add tests for it. This is important so I don't break it in a future version unintentionally.\n- Commit, do not mess with Rakefile or version (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)\n- Send me a pull request. Bonus points for topic branches.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjasl%2Factiveentity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjasl%2Factiveentity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjasl%2Factiveentity/lists"}