{"id":20061674,"url":"https://github.com/tylerrick/active_record_ignored_attributes","last_synced_at":"2025-05-05T16:30:47.457Z","repository":{"id":66442455,"uuid":"2237193","full_name":"TylerRick/active_record_ignored_attributes","owner":"TylerRick","description":"Allows you to compare Active Record objects based on their *attributes* (with same_attributes_as? and has_attribute_values?), to exclude some attributes from being used in comparison, and adds improved inspect method","archived":false,"fork":false,"pushed_at":"2015-03-04T22:27:43.000Z","size":132,"stargazers_count":14,"open_issues_count":2,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2023-04-11T10:51:25.522Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TylerRick.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-08-20T00:33:50.000Z","updated_at":"2020-07-08T01:07:17.000Z","dependencies_parsed_at":"2023-02-20T15:00:38.657Z","dependency_job_id":null,"html_url":"https://github.com/TylerRick/active_record_ignored_attributes","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TylerRick%2Factive_record_ignored_attributes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TylerRick%2Factive_record_ignored_attributes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TylerRick%2Factive_record_ignored_attributes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TylerRick%2Factive_record_ignored_attributes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TylerRick","download_url":"https://codeload.github.com/TylerRick/active_record_ignored_attributes/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224454258,"owners_count":17313916,"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":"2024-11-13T13:21:27.761Z","updated_at":"2024-11-13T13:21:28.471Z","avatar_url":"https://github.com/TylerRick.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"ActiveRecord Ignored Attributes\n===============================\n\nUpdated to work with RSpec 3.x\n\n\nAdds various behavior to Active Record models relating to the model's attributes:\n\n* Allows you to compare Active Record objects based on their *attributes*, which often makes more sense than the built-in `==` operator (which does its comparisons based on the `id` attribute alone! — not always what you want!)\n* You can configure which attributes, if any, should be excluded from the comparison\n* Provides a customizable inspect method, which by default excludes the same attributes that are excluded when doing a `same_attributes_as?` comparison\n\nExample\n=======\n\nConsider a User model that holds the notion of users that have a name.\n\n    ActiveRecord::Schema.define do\n      create_table :addresses, :force =\u003e true do |t|\n        t.string   :name\n        t.text     :address\n        t.string   :city\n        t.string   :state\n        t.string   :postal_code\n        t.string   :country\n        t.timestamps\n      end\n    end\n\n    class Address \u003c ActiveRecord::Base\n    end\n\n\nDefault ActiveRecord `==` behavior:\n\n    a = Address.new(address: 'B St.')\n    b = Address.new(address: 'B St.')\n    a == b # =\u003e false\n\nUsing `same_as?`:\n\n    a = Address.new(address: 'B St.')\n    b = Address.new(address: 'B St.')\n    a.same_as?(b) # =\u003e true\n\n    a = Address.new(address: 'B St.')\n    b = Address.new(address: 'Nowhere Road')\n    a.same_as?(b) # =\u003e false\n\nUsing `has_attribute_values?`:\n\n    a = Address.new(address: 'A St.', city: \"Don't care\")\n    a.has_attribute_values?(address: 'A St.')                    # =\u003e true\n    a.has_attribute_values?(address: 'A St.', city: 'Different') # =\u003e false\n\n    b = Address.new(address: 'B St.', city: \"Don't care\")\n    b.has_attribute_values?(address: 'A St.')                    # =\u003e false\n    b.has_attribute_values?({})                                  # =\u003e true\n\n\nInstalling\n==========\n\nAdd to your `Gemfile`:\n\n\u003cpre\u003e\ngem \"active_record_ignored_attributes\"\n\u003c/pre\u003e\n\nIf you want to *replace* the default ActiveRecord `==` operator with the `same_as?` behavior, you should be able to just override it, like this:\n\n    class ActiveRecord::Base\n      alias_method :==, :same_as?\n    end\n\nConfiguring which attributes are ignored\n========================================\n\nBy default, `id`, `created_at`, and `updated_at` will be ignored (`id` is *not* ignored by `inspect_without_ignored_attributes` though).\n\nIf you want to *add* some ignored attributes to the default array (`[:id, :created_at, :updated_at]`), you can override `self.ignored_attributes` like so, referencing `super`:\n\n    class Address \u003c ActiveRecord::Base\n      def self.ignored_attributes\n        super + [:name]\n      end\n    end\n\nIf you want to override the defaults instead of appending to them, just don't reference `super`:\n\n    class Address \u003c ActiveRecord::Base\n      def self.ignored_attributes\n        [:id, :name]\n      end\n    end\n\n`inspect_without_ignored_attributes`\n------------------------------------\n\nNow that you've declared which attributes you don't really care about, how about making it so you don't have to see them in your `inspect` output too? (The output from `inspect` is verbose enough as it is!!)\n\n`object.inspect_without_ignored_attributes` will give you the same output as the default `inspect` but without all those ignored attributes (except for `id` — `id` is always included, even if it's listed in `ignored_attributes`)):\n\n    address.inspect_without_ignored_attributes # =\u003e \"#\u003cAddress id: 1, address: nil, city: nil, country: nil, postal_code: nil, state: nil\u003e\"\n\n    # Compared to:\n    address.inspect                            # =\u003e \"#\u003cAddress id: 1, name: nil, address: nil, city: nil, state: nil, postal_code: nil, country: nil, created_at: \\\"2011-08-19 18:07:39\\\", updated_at: \\\"2011-08-19 18:07:39\\\"\u003e\"\n\nBut that is a lot to type every time. If you want inspect to *always* be more readable, you can override the ActiveRecord default like this:\n\n    class Address \u003c ActiveRecord::Base\n      alias_method :inspect, :inspect_without_ignored_attributes\n    end\n\nor even:\n\n    class ActiveRecord::Base\n      alias_method :inspect, :inspect_without_ignored_attributes\n    end\n\nCustomizable inspect method\n---------------------------\n\nIf you want to customize inspect further and specify exactly which attributes to show (and, optionally which delimiters to bracket the string with), you can use `inspect_with`:\n\n    class Address \u003c ActiveRecord::Base\n      def inspect\n        inspect_with([:city, :state, :country])\n      end\n    end\n\nor:\n\n    class Address \u003c ActiveRecord::Base\n      def inspect\n        inspect_with([:id, :name, :address, :city, :state, :postal_code, :country], ['{', '}'])\n      end\n    end\n\nIf you want to inspect with the same attributes as inspect_without_ignored_attributes plus some\n*additional* attributes (or, more likely, some virtual attributes), you can just use the\n`attributes_for_inspect` method that `inspect_without_ignored_attributes` uses, which automatically\nexcludes any attributes listed in `ignored_attributes`:\n\n    class Address \u003c ActiveRecord::Base\n      def inspect\n        inspect_with(attributes_for_inspect + [:virtual_attr_1, :virtual_attr_2])\n      end\n    end\n\nThis is useful because virtual attributes (methods in your model that aren't part of the\n\"attributes\" returned by record.attributes) won't be included by inspect_without_ignored_attributes\nby default.\n\nRSpec\n=======\n\nThis gem comes with a `be_same_as` and `have_attribute_values` matcher for RSpec.\n\nAdd this to your spec_helper.rb:\n\n    require 'active_record_ignored_attributes/matchers'\n\nThen in your specs you can write such nicely readable expectations as:\n\n    expected = Address.new({city: 'City', country: 'USA'})\n    Address.last.should be_same_as(expected)\n\n    a = Address.new(address: 'B St.')\n    b = Address.new(address: 'B St.')\n    a.should be_same_as?(b) # passes\n\n    a = Address.new(address: 'B St.')\n    b = Address.new(address: 'Nowhere Road')\n    a.should be_same_as?(b) # fails\n\nand it will lovingly do a diff for you and only show you the attributes in each object that actually differed:\n\n    expected: #\u003cAddress address: \"Nowhere Road\"\u003e\n         got: #\u003cAddress address: \"B St.\"\u003e\n\nOr use `should have_attribute_values` whenever it's more convenient to specify the expected attributes with a hash instead of building a new model instance:\n\n    a = Address.new(               name: 'A', address: 'The Same Address', city: \"Don't care\")\n    a.should have_attribute_values name: 'A', address: 'A Slightly Different Address'\n\nwill fail with:\n\n    expected: {:name=\u003e\"A\", :address=\u003e\"A Slightly Different Address\"}\n         got: {:name=\u003e\"A\", :address=\u003e\"The Same Address\"}\n\nMotivation\n==========\n\nThe default ActiveRecord `==` behavior isn't always adequate, as you can probably tell from the example at the top, or by the fact that you are looking at this gem right now.\n\nThis is the implementation of `==` in [ActiveRecord](https://github.com/rails/rails/blob/3-0-10/activerecord/lib/active_record/base.rb):\n\n    # Returns true if +comparison_object+ is the same exact object, or +comparison_object+\n    # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.\n    #\n    # Note that new records are different from any other record by definition, unless the\n    # other record is the receiver itself. Besides, if you fetch existing records with\n    # +select+ and leave the ID out, you're on your own, this predicate will return false.\n    #\n    # Note also that destroying a record preserves its ID in the model instance, so deleted\n    # models are still comparable.\n    def ==(comparison_object)\n      comparison_object.equal?(self) ||\n        (comparison_object.instance_of?(self.class) \u0026\u0026\n          comparison_object.id == id \u0026\u0026 !comparison_object.new_record?)\n    end\n\nThat implementation is often fine when you are dealing with saved records, but isn't helpful *at all* when one or both of the objects being compared is not-yet-saved.\n\nIf you want to compare two model instances based on their attributes, you will probably want to *exclude* certain irrelevant attributes from your comparison, such as: `id`, `created_at`, and `updated_at`. (I would consider those to be more *metadata* about the record than part of the record's data itself.)\n\nThis might not matter when you are comparing two new (unsaved) records (since `id`, `created_at`, and `updated_at` will all be `nil` until saved), but I sometimes find it necessary to compare a *saved* object with an *unsaved* one (in which case == would give you false since nil != 5). Or I want to compare two *saved* objects to find out if they contain the same *data* (so the ActiveRecord `==` operator doesn't work, because it returns false if they have different `id`'s, even if they are otherwise identical).\n\nSee also: http://stackoverflow.com/questions/4738439/how-to-test-for-activerecord-object-equality\n\n\nQuestions and Ideas\n===================\n\n* Does such a gem already exist?\n  * All I've found so far is https://github.com/GnomesLab/active_record_attributes_equality\n* What should the method be called? I don't think overriding the existing `==` operator is a good idea (as done in [active_record_attributes_equality](https://github.com/GnomesLab/active_record_attributes_equality)), so for now I'm calling it `same_attributes_as?` (aliased as `same_as?` since that's easier to type). Other runners up were `practically_same_as?` and `attributes_eql?`.\n\n\nPossible improvements:\n\n* Allow the default to be overridden with a class macro like `ignore_for_attributes_eql :last_signed_in_at, :updated_at`\n\nAlso, perhaps you want to set the default ignored attributes for a model but still wish to be able to override these defaults as needed...\n\n    address.same_as?(other_address, :ignore =\u003e [:addressable_type, :addressable_id])\n    address.same_as?(other_address, :only =\u003e [:city, :state, :country])\n\n\nContributing\n============\n\nComments and contributions are welcome.\n\nPlease feel free to fork the project at http://github.com/TylerRick/active_record_ignored_attributes and to send pull requests.\n\nBugs can be reported at https://github.com/TylerRick/active_record_ignored_attributes/issues\n\nLicense\n=======\n\nCopyright 2011, Tyler Rick\n\nThis is free software, distributed under the terms of the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftylerrick%2Factive_record_ignored_attributes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftylerrick%2Factive_record_ignored_attributes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftylerrick%2Factive_record_ignored_attributes/lists"}