How to reload initializer for each record update - ruby-on-rails

In my application I am using one global variable which is defined in config/initializers/details.rb
$available_plans= Plan.all
and using this variable, all over the application and this will be available to all users. If I add one more plan, that should be reflected automatically, so I need to reload initializer
so after_create added a method to reload the initializer,
ActiveSupport::Dependencies.load_file "config/initializers/details.rb"
is this the right way ?

Why not:
class Plan < ActiveRecord::Base
after_create: custom_method
# Whatever code
private
def custom_method
$available_plans= Plan.all
end
end
Also, when a plan is edited that should be reflected in the global variable, so we should use after_save instead:
class Plan < ActiveRecord::Base
after_save: custom_method
# Whatever code
private
def custom_method
$available_plans= Plan.all
end
end

Another way to handle this is to forgo the initializer and global variables entirely. This, to my eye, is more localized and cleaner.
First, in your Plan class, add a caching method to access all the plans:
def self.available_plans
#available_plans ||= Plan.all
end
Note: This can be improved using the cached_method gem which will let you cache the plans across app instances etc... if that is important to you.
Now you can access your plans throughout the code like this:
Plan.available_plans
The first time this is called in an app instance, the plans will be loaded. Every time thereafter it will return the original results.
Finally, when plans are added, removed, or modified (again, in your Plan model):
after_save :expire_cached_available_plans
after_destroy :expire_cached_available_plans
...
private
def expire_cached_available_plans
self.class.instance_variable_set("#available_plans", nil)
end
Now whenever you create, update, or destroy an Plan object, the cached plans will be cleared. The next time you call Plan.available_plans, it will fetch (and cache) a fresh list.

Related

How to skip before filter in model with ruby on rails 5.2?

My application manages a hierarchy of documents. Each document has a hierarchycal index, which is calculated at creation only. File document.rb starts with
class BusinessRule < ActiveRecord::Base
### before filter
before_create :set_hierarchy
and the hierarchy is calculated based on parent and brothers so that self.hierarchy = last_one.next is evaluated in the scope of the parent.
Now, I add the version management feature. Thanks to a new_version method added to the controller, a document is duplicated using the #document.dup method, and then it is saved: the hierarchy is supposed to remain the same, and only the version number needs to be incremented.
Fine.
But the before_create filter is triggered by the save action in the model, and the hierarchy is incremented, which does not fit the requirements.
How can I prevent the before filter in the model from triggering in the case of the new_version action in the controller?
I'm not sure if this is the best way to do this, but I'd do something like this.
class BusinessRule < ActiveRecord::Base
attr_accessor :skip_set_hierarchy
before_action :set_hierarchy, unless: :skip_set_hierarchy
...
end
Now, if you don't want the callback to be triggered, you can set that to true on demand:
def new_version
business_rule = BusinessRule.new business_rule_params
business_rule.skip_set_hierarchy = true
business_rule.save
#this can be refactored a lot (set the skip_set_hierarchy to true inside params, use create instead of new, I made it verbose on purpose to make it clearer)
end
ActiveRecord will skip the callback because skip_set_hierarchy will return true. You don't need to change the rest of the code, since by default it will return nil.
I think this is the good case to use skip_callback method:
BusinessRule.skip_callback(:create, :before, :set_hierarchy)
# your code to create BusinessRule objects without setting hierarchy
# ...
BusinessRule.set_callback(:create, :before, :set_hierarchy)
If you're going to skip/set callbacks quite often you could simplify it using special helping method:
# config/initializers/without_callback.rb
module ActiveSupport::Callbacks::ClassMethods
def without_callback(*args, &block)
skip_callback(*args)
yield
set_callback(*args)
end
end
And you will be able to skip a callback like this:
BusinessRule.without_callback(:create, :before, :set_hierarchy) do
# your code to create BusinessRule objects without setting hierarchy
# ...
end

Cache API responses until told to release?

I have the following model:
class User < ActiveRecord::Base
def stripe_plan_id
self.stripe_subscription.plan.id
end
def stripe_subscription
customer = Stripe::Customer.retrieve(self.stripe_customer_id)
customer.subscriptions.retrieve(self.stripe_subscription_id)
end
end
I've got the model attribute stripe_plan_id I don't want to it persist in the database using ActiveRecord, but I need to check this parameter a lot. I would like to cache it on my server, and flush it on demand. Ideally I don't want to use redis.
What's the best approach? I'm trying to work out if ||= can be used somehow.
There are alot of ways, i'm assuming you know how to cache already using whatever you have (memcached, redis, rails.cache, etc), let me know if that is not the case. I recommend creating a caching class that gets or reloads cache based on an id. Your memoizing wouldn't work for your case because you want to persist between controllers.
def StripeCache < Struct.new(:user)
def cache_key
"#{user.id}_stripe_plan_id"
end
def get
ReadFromCache(cache_key)
end
def reload
WriteToCache(cache_key, user.stripe_plan_id)
end
end
Then when you want to get this you just call
StripeCache.new(user).get
and to save new stuff you call
StripeCache.new(user).reload
Is a little extra overhead, but will abstract all the reading and writing to and from cache from you.

Mongoid 3 callbacks: before_upsert vs. before_save

For Mongoid 3+, is there a diagram/description of the various callbacks?
http://mongoid.org/en/mongoid/v3/callbacks.html
For example, what's the difference between before_upsert vs. before_save. Isn't a save caused by an insert or update call? Or does save also get called by destroy?
Also, what's difference between before_xxx and around_xxx?
Cheers,
With before_xxx the code is executed before the action and with around_xxx you have the option to execute code before and after the action itself.
For example, imagine you want to update all the user belongings after destroy a user project (User has_many :proyects and Project belongs_to User) :
class ProjectsController < ApplicationController
around_destroy :destroy_belongings
def destroy_belongings
old_user = self.user
...
# Here the before_destroy ends.
yield # Here the destroy is performed itself.
# Here the after_destroy starts. It's needed to do this operation after destroy the project because, imagine, the update_belongings method calculates something related to the current number of proyects. And a simple after_destroy is not useful as we would have lost the project owner.
old_user.update_belongings
end
end
You can also see related answers here and here. Moreover this other article could be useful for you.

Instance Variables on Active Record Models

I've created an instance variable on an ActiveRecord Model where I want to save a bit of computationally heavy data in each instance... Here's my code to do that:
class Account < ActiveRecord::Base
after_initialize :init
attr_accessor :market_value
def init
self.market_value ||= my_lengthy_function
end
end
where I'll take the hit to get that instance data (market_value) run when I init an instance of the model.
This works - I can see how I don't have to re-calculate my market_value property.
My problem is, when I access that object through another context, rails doesn't leverage that data as I'd expect.
For example:
Say I create an instance of an account (a = Account.find_by_id(2)). That market_value will be calculated on that object once.
If I have a nested has_many relationship to something called "holdings" (not in my sample code) on that account object, I'm going to want each of those holding objects (a holding) to be able to use it's parent account object.
However, in my code, I access the account from it's nested holding objects (my_holding.account.market_value) - I re-instantiate an instance of that account object, and incur that costly computation, even though it's already been computed.
How can I better leverage that account market_value property so that it doesn't keep recalculating?
I would not put the calculation logic in the ActiveRecord model. Maybe something along these lines:
class MarketValueCalculator
def initialize()
#market_values = {}
end
def calculate_for_account(account)
#market_values[account.id] ||= heavy_lifting
end
def heavy_lifting
###
end
end
#calculator = MarketValueCalculator.new
#market_value = #calculator.calculate_for_account(account)
#market_value = #calculator.calculate_for_account(my_holding.account)
i would build up a simple cache on class-level with the model ids as keys:
class Account < ActiveRecord::Base
def market_value
##market_value ||= {}
##market_value[id] ||= my_lengthy_function
end
end
not tested if this would work though, especially with class reloading in development.

How does one set persistent instance variables on ActiveRecord objects without an identity map?

I've been knocking my head against a problem for ages until I realised what was going on.
I want to have a committee which must always have at least one member. To achieve this, each member checks it's not the last member before being destroyed.
The code below should prevent the last member being destroyed UNLESS the committee itself is being destroyed in which case it happily self-destructs.
class Committee < ActiveRecord::Base
has_many :committee_members
before_destroy: { #destroy_initiated = true }
def destroy_initiated?
#destroy_initiated
end
end
class CommitteeMember < ActiveRecord::Base
belongs_to :committee
before_destroy :ensure_not_last
def ensure_not_last
unless self.committee.destroy_initiated?
if self.committee.committee_members.count == 1
raise 'You cannot remove the last committe member. Try destroying the committee instead'
end
end
end
end
The problem
The problem is that each CommitteeMember references a different instance of the Committee object, they all have different object identities:
e.g. #<Committee:0x00000105c41f20> v. #<Committee:0x00000105c2c3a0>
This means that even when I set #destroy_initiated to be true on once instance of Committee with ID 20, it's not going to be set to true on the instance referenced by one of its committee_members.
Leaving aside Rails 3.1 which I know has an identity map, is there a clean workaround to having an instance variable which is available on all instantiations of Committee?
I'm considering doing a class variable containing a map of destroy_initiated? to each Committee ID but this feels pretty messy.
I'm not sure if I can answer this question properly without more context, but I can give you a possible solution to think about...
In an app I just recently put in production, we would essentially cache an object as an instance variable on ApplicationController. Then whenever we needed it, we simply ask for the instance variable rather than finding it with Active Record.
class ApplicationController < ActionController::Base
before_filter :set_committee
def set_committee
#committee ||= Committee.new()
end
end
So now, for the duration of the request, anything inheriting from ApplicationController can access the #committee object. If you can use a similar pattern (doesn't have to be application controller, could just be any other controller) you would essentially have a "global" variable for the duration of the request.

Resources