Skip to content

Add dirty methods for store accessors #19333

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 31, 2019
Merged

Add dirty methods for store accessors #19333

merged 2 commits into from
Mar 31, 2019

Conversation

palkan
Copy link
Contributor

@palkan palkan commented Mar 14, 2015

Example:

class User < AR:Base
  store_accessor :settings, :color
end

u = User.new
u.color_changed? #=> false

u.color = 'red-n-white'
# Or
u.settings['color'] = 'red-n-white'

u.color_changed? #=> true
u.color_was #=> nil
u.color_change #=> [nil, 'red-n-white']

Also, force Model.stored_attributes[:store] array to contain string keys.

@palkan palkan force-pushed the dirty-store branch 2 times, most recently from 29d7752 to 942fe13 Compare June 18, 2015 09:38
@palkan
Copy link
Contributor Author

palkan commented Jun 18, 2015

@rafaelfranca @sgrif Any feedback on that?

@sgrif
Copy link
Contributor

sgrif commented Jun 18, 2015

I think that this falls into the bucket of "we should just promote store accessors to proper attributes".

@palkan
Copy link
Contributor Author

palkan commented Jun 18, 2015

@sgrif Interesting. Is anyone struggling with that?

@palkan
Copy link
Contributor Author

palkan commented Apr 12, 2017

Just found myself struggling with the absence of accessors dirty methods. Maybe, we should revamp this PR? It's pretty simple but useful change, IMO.

/cc @rafaelfranca @matthewd

@palkan
Copy link
Contributor Author

palkan commented Mar 20, 2019

Years have passed. I'm still missing this 😢

Maybe, Rails 6 is a good place for this feature? @kaspth WDYT?

define_method("#{key}_changed?") do
return false unless attribute_changed?(store_attribute)
prev_store, new_store = changes[store_attribute]
prev_store.try(:[], key) != new_store.try(:[], key)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go with &.dig(key) instead of the try.

# u.color_changed? # => true
# u.color_was # => 'black'
# u.color_change # => ['black', 'red']
#
# # Add additional accessors to an existing store through store_accessor
# class SuperUser < User
# store_accessor :settings, :privileges, :servants
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These'll also get change tracking too, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain, what did you mean here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's basically what you demonstrate in your PR description. I'm just asking if store_accessor :color also adds color_changed? etc. or if we missed it and only store :settings, accessors: %i( color ) would get it.

@palkan palkan force-pushed the dirty-store branch 2 times, most recently from 65698c8 to 619c038 Compare March 25, 2019 22:41
@palkan
Copy link
Contributor Author

palkan commented Mar 25, 2019

@kaspth Hey! Thanks for the feedback!

I've addressed all the comments and actualized the PR by adding saved changes helpers as well.

@kaspth kaspth merged commit ba4e74e into rails:master Mar 31, 2019
@LucasArruda
Copy link

This feature proves you should never ever give up! 👍

@wbcasey
Copy link

wbcasey commented Jun 27, 2020

What's the easiest way to backport this to Rails 4.2?

@palkan
Copy link
Contributor Author

palkan commented Jun 29, 2020

@wbcasey You can patch ActiveRecord::Store::ClassMethods#store_accessor method to add additional methods. Smth like:

ActiveSupport.on_load(:active_record) do
  ActiveRecord::Store::ClassMethods.prepend(Module.new do
    def store_accessor(store_name, *keys)
      super
      keys = keys.flatten
      _store_accessors_module.module_eval do
          keys.each do |key|
            define_method("#{key}_changed?") do
              return false unless attribute_changed?(store_name)
              prev_store, new_store = changes[store_name]
              prev_store&.dig(key) != new_store&.dig(key)
            end
          end
          # ...
       end
    end
  end)
end

@LucasArruda
Copy link

@wbcasey defining those methods there?

@wbcasey
Copy link

wbcasey commented Jun 29, 2020

@LucasArruda basically back-porting the "dirty" functionality. I tried just bringing it in, but some of the API surface has changed between 4.2 and 6.x

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants