Rails model call different class based on user flag - ruby-on-rails

Learning Rails, I just faced something where some lights could be helpful.
I have the classes, A, B, C that all do an action.
And I have a Message model and I would like, when I am going to save, to call one of those classes, based on the user output.
I am struggling now on what would be the more rubyist way to write the code for the model but also the classes, depending on the model method.
Option A:
case #user.flag:
when 'alpha'
A.new(message)
when 'beta'
B.new(message)
when 'gamma'
C.new(message)
Option B:
Moving A,B,C from classes to user flag Instance methods of a Module called Functions
Functions.send(#user.flag.to_sym,message)
Since I have little knowledge of Rails, I am looking for how to write the most clean and reusable code. Thanks in advance.

As with many design decisions, there's numerous approaches you could, each of which would be "correct" mostly based on preference. Here's how I'd do it.
Firstly, I'd make sure #user.flags can only take on certain values since its value is being used to decide other actions. In Ruby the generally accepted way of handling these values is also as symbols since a given symbol is immutable.
Secondly, since you're doing something with the Message model after it's saved you can utilize the after_save callback and keep the action inside the Message model itself. This makes it more tied to the message model and makes it more readable in general.
Lastly, you'll want some sort of guarantee that your save/transaction rolls back if there's an error with your after_save action. Going off this answer you can do that by raising an error in `after_save_
In app/models/user.rb
class User < ActiveRecord::Base
FLAGS = %w[alpha beta gamma].freeze
# Ensuure that `flag` field can only take on certain pre-defined values
# Also validate that flag can never be nil. You may need to change that
# as needed for your application
validates :flag, presence: true, inclusion: FLAGS
def flag
# This method isn't 100% necessary but I like to personally follow
# the pracitce of returning symbols for enumerated values
super(flag).try(:to_sym)
end
end
In app/models/message.rb
class Message < ActiveRecord::Base
after_save :post_process_message
private
# I'd recommend a better name for this method based on what you're
# specifically doing
def post_process_message
# Notice the more descriptive method name
# Also no need to pass `message` as a param since it's now located
# inside this model. You could also move it to a separate class/service
# as needed but don't over-optimize until you need to
send("handle_post_process_for_flag_#{user.flag}")
rescue StandardError => e
# Something went wrong, rollback!
# It isn't "great practice" to rescue all errors so you may want to replace
# this with whatever errrors you excpect your methods to throw. But if you
# need to, it's fine to be conservative and rescue all on a case-by-case
# basis
raise ActiveRecord::RecordInvalid.new(self)
end
def handle_post_process_for_flag_alpha
end
def handle_post_process_for_flag_beta
end
def handle_post_process_for_flag_gamma
end
end

This is an interesting question, as #user2490003 said, there is no write/wrong way of doing this.
Your approach will change depending on how your A,B and C classes implement and their representation and also what your method does in each class.
Let's take an example, a method called talk and two classes Man, Women.
So, you may implement this as
Individual class methods
class Man
def talk
# talk like an adult
end
end
class Women
def talk
# talk like an adult
end
end
However as you can see, this talk method is same for both Man and Women and also you can see they both normally share the same functionalities and attributes. So, create a base class called Human and move the talk method there
As a base class method
class Human
def talk
# talk like an adult
end
end
class Man < Human
end
class Woman < Human
end
Now let's get an example of a baby and say baby talk differently than Man and Woman, although baby still inherits from Human. In such cases you can do
class Baby < Human
def talk
# baby talk
end
end
What happens here is, Baby will inherit from Human , but when you call
Baby.new.talk # => baby talk
it execute the talk method in Baby class (not in the Human class)
Extracting the method to a module
Let's get a Parrot class, and assume that it has a talk method too, and also it's same as Human talk.
Now the problem we have is we cannot inherit Parrot class from Human, but we still want to have the code in the talk method. In a case like that, you can use a Module, so, you can do
module Talkable
def talk
# talk like an adult
end
end
class Human
include Talkable
end
class Parrot
include Talkable
end
As, as I explained (or at least tried..), your implementation will depend on how your class A,B,C and Message classes are related.
What I personally do in situations like this is, get a pen and paper and try to map these objects without thinking about how to implement then in ruby or any language. Once you have an idea on how they all hang together, it's easy to find the syntax to implement it

Related

before and after method call on models in ruby on rails, do "thing"

I'm pondering a way to wrap method calls on models in RoR. (The reason for this being a custom model caching system that would allow me to do a lot of work behind the scenes on data being created by some large models that my clients are using without having to constantly load those models and parse ALL the very fat data to get the output. I could just send the output already calculated and stored in a different place and a delayed job behind the scenes that does the calculation and stores it isn't really an option due to how the data is collected or the egregious volume in question.. Its hard to explain.)
So given that I have an instance of this extremely simple class;
class Person < ApplicationRecord
attr_accessor: :name
has_many :books
has_many :friends
end
How would I go about writing something that allows me to programmatically "intercept" ALL (or all except those I specifically say not to) method calls to a given class.
So if someone does this;
bob = Person.find 1
puts bob.name
puts bob.friends.count
I could have this system know to do something before .name is called or before .friends is called. Something like checking to see if we already have an answer to this question somewhere else..
I was looking at using a before_hook and override the method_added hook to prepend my code per this question: call before methods in model on ruby but the issue was it works for .name but not for .friends. I assume this is due to some rails magic as friends just gives you the active record call per normal without actually executing the hook..
I'd love to be able to do something like;
class Person < ApplicationRecord
include CustomCachableThingy
before_hook :friends, :name, except: :books
attr_accessor: :name
has_many :books
has_many :friends
end
Again this is a very simple example. The models in question are way too big and have way too much data. So trying to find something I can make work for my client.
Thoughts?
If you really want to wrap every method call (or even the ones defined by the target class itself), you can define a module that does 2 things,
Get all the instance_methods and wraps them with your custom code
Override the method_added method call and wrap every new method that is added with the same custom code
A good example using this strategy is answered here. You can customize this to take additional except configuration and exclude those methods.
The meta-programming going on here is to rewrite each method as it is injected into the instance.
Another option is to wrap only specific method calls with your custom code, if you know which ones you'll need to cache. It will be less overhead and cleaner implementation.
For implementation, you can use the same meta-programming approach as described in the first example. One more approach can be to add an alias to the original method and rewrite the method to include your hook. Something like,
def add_before_hook(method_name)
class_eval do
without = :"#{method_name}_without_before_each_method"
alias_method without, method_name
define_method method_name do |*args, &block|
puts 'called before'
send without, *args, &block
end
end
end
The problem with the second approach is that it pollutes your method namespace by adding a duplicate method for each of your instance methods.

How to keep history in rails model

There are three questions here, all of them set in bold.
I have a personnel database where historical data is also stored.
Each table has two columns start and stop which indicate the period during which this fact is valid.
When any field of a record changes, a new record is created with start=today, and stop=nil,
and the old record is given a stop=today-1 (or whatever the effective date of the change is instead of today).
When a record is to be deleted, instead of deleting, we simply set stop=today.
This way, it is easy to
give a history of any employee
give a snapshot of what things looked like on a particular date
Firstly this pattern seems common enough that probably I should not need to re-invent the wheel.
Yet in my search all I found were some gems called auditable or papertrail.
From a first glance, they did not seem to do what I want, but perhaps I am mistaken.
Do you know of a gem that does something similar?
If I don't find a solution, what is enough for my purposes is to have a few methods which works for all my models, like
def when(date)
self.where("start <= ? and ? <= stop", ondate, ondate)
end
I thought about inheriting
class MyRecord < ActiveRecord::Base
def when ...
end
end
and having all my models inherit from MyRecord, but this broke everything. Why?
I could just add my methods to ActiveRecord::Base, but that seems wrong.
What is the proper way to add a method to all models?
Answer
Parts 2 and 3 of this question have been answered before here. Since that contains Josh's answer (which I +1ed), as well as why inheriting from MyRecord doesn't work, I did not accept Josh's answer.
I still have not located a gem which maintains a good history, but two out of three ain't bad...
This sounds to me like an excellent opportunity to use an ActiveSupport::Concern. You could write something like:
History.rb
require 'active_support/concern'
module History
extend ActiveSupport::Concern
included do
def self.when(date)
self.where("start <= ? and ? <= stop", ondate, ondate)
end
end
end
Your model
class SomeClass < ActiveRecord::Base
include History
end
You could then call SomeClass.when(some_date_string) on any models that you need to include history on.

before_create outside of model

I am newbie in Rails but have quite an extensive amount of experience coding in other language (such as Java). Well, I liked Ruby's metaprogramming.
Usually, when in my way of learning Rails... I see some plug in such as public_activity, or others that can simply do
include Namespace::SomeModel
And that, in the case of public_activity, their SomeModel's method is somehow called on before the record is created. I suspect that before_create is included in SomeModel. So... I begin to experiment but was stuck that, obviously, the before_create become unavailable to the SomeModel class I am having that is located in directory manually auto-loaded on rails s.
What I want to ask if, how can just by including SomeModel, one of its method is called on ActiveRecord create event?
I just downloaded the source code of the code, but well... it will takes time. And, just to anticipate I cannot found the answer; you guys are my better teacher than silent code. So, give me time to answer even for slightest hint. Thanks.
Rails allows to extend the class you're including a module in. Basic techniques are described in http://api.rubyonrails.org/classes/ActiveSupport/Concern.html#method-i-included
This allows to "set up" a module, like
module Foo
extend ActiveSupport::Concern
included do
# Within this area "self" now refers to the class in which the module is included
# Every method you now call is called agains the class
# As such you can now write things like
validates_inclusion_of ...
define_method ...
end
end
It is a quite famous procedure
Module NewModule
extend ActiveSupport::Concern
def self.included(base)
base.after_create :a_method
end
def a_method
# your_code_here
end
end
class A < ActiveRecord::Base
include NewModule
end
with ActiveSupport::Extend you give NewModule's class and instance methods to A accordingly.
The NewModule.included code is executed when NewModule is included to another class.

Rails Observer Alternatives for 4.0

With Observers officially removed from Rails 4.0, I'm curious what other developers are using in their place. (Other than using the extracted gem.) While Observers were certainly abused and could easily become unwieldily at times, there were many use-cases outside of just cache-clearing where they were beneficial.
Take, for example, an application that needs to track changes to a model. An Observer could easily watch for changes on Model A and record those changes with Model B in the database. If you wanted to watch for changes across several models, then a single observer could handle that.
In Rails 4, I'm curious what strategies other developers are using in place of Observers to recreate that functionality.
Personally, I'm leaning towards a sort of "fat controller" implementation, where these changes are tracked in each models controller's create/update/delete method. While it bloats the behavior of each controller slightly, it does help in readability and understanding as all the code is in one place. The downside is that there's now code that is very similar scattered throughout several controllers. Extracting that code into helper methods is an option, but you're still left with calls to those methods littered everywhere. Not the end of the world, but not quite in the spirit of "skinny controllers" either.
ActiveRecord callbacks are another possible option, though one I don't personally like as it tends to couple two different models too closely together in my opinion.
So in the Rails 4, no-Observers world, if you had to create a new record after another record was created/updated/destroyed, what design pattern would you use? Fat controllers, ActiveRecord callbacks, or something else entirely?
Thank you.
Take a look at Concerns
Create a folder in your models directory called concerns. Add a module there:
module MyConcernModule
extend ActiveSupport::Concern
included do
after_save :do_something
end
def do_something
...
end
end
Next, include that in the models you wish to run the after_save in:
class MyModel < ActiveRecord::Base
include MyConcernModule
end
Depending on what you're doing, this might get you close without observers.
They are in a plugin now.
Can I also recommend an alternative which will give you controllers like:
class PostsController < ApplicationController
def create
#post = Post.new(params[:post])
#post.subscribe(PusherListener.new)
#post.subscribe(ActivityListener.new)
#post.subscribe(StatisticsListener.new)
#post.on(:create_post_successful) { |post| redirect_to post }
#post.on(:create_post_failed) { |post| render :action => :new }
#post.create
end
end
My suggestion is to read James Golick's blog post at http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html (try to ignore how immodest the title sounds).
Back in the day it was all "fat model, skinny controller". Then the fat models became a giant headache, especially during testing. More recently the push has been for skinny models -- the idea being that each class should be handling one responsibility and a model's job is to persist your data to a database. So where does all my complex business logic end up? In business logic classes -- classes that represent transactions.
This approach can turn into a quagmire (giggity) when the logic starts getting complicated. The concept is sound though -- instead of triggering things implicitly with callbacks or observers that are hard to test and debug, trigger things explicitly in a class that layers logic on top of your model.
Using active record callbacks simply flips the dependency of your coupling. For instance, if you have modelA and a CacheObserver observing modelA rails 3 style, you can remove CacheObserver with no issue. Now, instead say A has to manually invoke the CacheObserver after save, which would be rails 4. You've simply moved your dependency so you can safely remove A but not CacheObserver.
Now, from my ivory tower I prefer the observer to be dependent on the model it's observing. Do I care enough to clutter up my controllers? For me, the answer is no.
Presumably you've put some thought into why you want/need the observer, and thus creating a model dependent upon its observer is not a terrible tragedy.
I also have a (reasonably grounded, I think) distaste for any sort of observer being dependent on a controller action. Suddenly you have to inject your observer in any controller action (or another model) that may update the model you want observed. If you can guarantee your app will only ever modify instances via create/update controller actions, more power to you, but that's not an assumption I would make about a rails application (consider nested forms, model business logic updating associations, etc.)
Wisper is a great solution. My personal preference for callbacks is that they're fired by the models but the events are only listened to when a request comes in i.e. I don't want callbacks fired while I'm setting up models in tests etc. but I do want them fired whenever controllers are involved. This is really easy to setup with Wisper because you can tell it to only listen to events inside a block.
class ApplicationController < ActionController::Base
around_filter :register_event_listeners
def register_event_listeners(&around_listener_block)
Wisper.with_listeners(UserListener.new) do
around_listener_block.call
end
end
end
class User
include Wisper::Publisher
after_create{ |user| publish(:user_registered, user) }
end
class UserListener
def user_registered(user)
Analytics.track("user:registered", user.analytics)
end
end
In some cases I simply use Active Support Instrumentation
ActiveSupport::Notifications.instrument "my.custom.event", this: :data do
# do your stuff here
end
ActiveSupport::Notifications.subscribe "my.custom.event" do |*args|
data = args.extract_options! # {:this=>:data}
end
My alternative to Rails 3 Observers is a manual implementation which utilizes a callback defined within the model yet manages to (as agmin states in his answer above) "flip the dependency...coupling".
My objects inherit from a base class which provides for registering observers:
class Party411BaseModel
self.abstract_class = true
class_attribute :observers
def self.add_observer(observer)
observers << observer
logger.debug("Observer #{observer.name} added to #{self.name}")
end
def notify_observers(obj, event_name, *args)
observers && observers.each do |observer|
if observer.respond_to?(event_name)
begin
observer.public_send(event_name, obj, *args)
rescue Exception => e
logger.error("Error notifying observer #{observer.name}")
logger.error e.message
logger.error e.backtrace.join("\n")
end
end
end
end
(Granted, in the spirit of composition over inheritance, the above code could be placed in a module and mixed in each model.)
An initializer registers observers:
User.add_observer(NotificationSender)
User.add_observer(ProfilePictureCreator)
Each model can then define its own observable events, beyond the basic ActiveRecord callbacks. For instance, my User model exposes 2 events:
class User < Party411BaseModel
self.observers ||= []
after_commit :notify_observers, :on => :create
def signed_up_via_lunchwalla
self.account_source == ACCOUNT_SOURCES['LunchWalla']
end
def notify_observers
notify_observers(self, :new_user_created)
notify_observers(self, :new_lunchwalla_user_created) if self.signed_up_via_lunchwalla
end
end
Any observer that wishes to receive notifications for those events merely needs to (1) register with the model that exposes the event and (2) have a method whose name matches the event. As one might expect, multiple observers can register for the same event, and (in reference to the 2nd paragraph of the original question) an observer can watch for events across several models.
The NotificationSender and ProfilePictureCreator observer classes below define methods for the events exposed by various models:
NotificationSender
def new_user_created(user_id)
...
end
def new_invitation_created(invitation_id)
...
end
def new_event_created(event_id)
...
end
end
class ProfilePictureCreator
def new_lunchwalla_user_created(user_id)
...
end
def new_twitter_user_created(user_id)
...
end
end
One caveat is that the names of all events exposed across all the models must be unique.
I think the the issue with Observers being deprecated is not that observers were bad in and of themselves but that they were being abused.
I would caution against adding too much logic in your callbacks or simply moving code around to simulate the behavior of an observer when there is already a sound solution to this problem the Observer pattern.
If it makes sense to use observers then by all means use observers. Just understand that you will need to make sure that your observer logic follows sound coding practices for example SOLID.
The observer gem is available on rubygems if you want to add it back to your project
https://github.com/rails/rails-observers
see this brief thread, while not full comprehensive discussion I think the basic argument is valid.
https://github.com/rails/rails-observers/issues/2
You could try https://github.com/TiagoCardoso1983/association_observers . It is not yet tested for rails 4 (which wasn't launched yet), and needs some more collaboration, but you can check if it does the trick for you.
How about using a PORO instead?
The logic behind this is that your 'extra actions on save' are likely going to be business logic. This I like to keep separate from both AR models (which should be as simple as possible) and controllers (which are bothersome to test properly)
class LoggedUpdater
def self.save!(record)
record.save!
#log the change here
end
end
And simply call it as such:
LoggedUpdater.save!(user)
You could even expand on it, by injecting extra post-save action objects
LoggedUpdater.save(user, [EmailLogger.new, MongoLogger.new])
And to give an example of the 'extras'. You might want to spiffy them up a bit though:
class EmailLogger
def call(msg)
#send email with msg
end
end
If you like this approach, I recommend a read of Bryan Helmkamps 7 Patterns blog post.
EDIT: I should also mention that the above solution allows for adding transaction logic as well when needed. E.g. with ActiveRecord and a supported database:
class LoggedUpdater
def self.save!([records])
ActiveRecord::Base.transaction do
records.each(&:save!)
#log the changes here
end
end
end
It's worth mentioning that Observable module from Ruby standard library cannot be used in active-record-like objects since instance methods changed? and changed will clash with the ones from ActiveModel::Dirty.
Bug report for Rails 2.3.2
I have the same probjem! I find a solution ActiveModel::Dirty so you can track your model changes!
include ActiveModel::Dirty
before_save :notify_categories if :data_changed?
def notify_categories
self.categories.map!{|c| c.update_results(self.data)}
end
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html

ActiveRecord: make all text fields have strip called on them before saving, unless specified otherwise

I've ran into various problems with various sites over the years with users putting spaces at the start/end of string and text fields. Sometimes these cause formatting/layout problems, sometimes they cause searching problems (ie search order looking wrong even though it isn't really), sometimes they actually crash the app.
I thought it would be useful, rather than putting in a bunch of before_save callbacks as i have done in the past, to add some functionality to ActiveRecord to automatically call .strip on any string/text fields before saving, unless i tell it not to, eg with do_not_strip :field_x, :field_y or something similar at the top of the class definition.
Before i go and figure out how to do this, has anyone seen a nicer solution? Just to be clear, i already know that i can do this:
before_save :strip_text_fields
def strip_text_fields
self.field_x.strip!
self.field_y.strip!
end
but i'm looking for a nicer way.
cheers, max
Here's a handy module that you could drop into lib and include in your models. It doesn't have the exceptions that you mentioned, but it looks for a strip! method which might be good enough. You could add the exceptions feature fairly easily, if needed.
# lib/attribute_stripping.rb
module AttributeStripping
def self.included(context)
context.send :before_validation, :strip_whitespace_from_attributes
end
def strip_whitespace_from_attributes
attributes.each_value { |v| v.strip! if v.respond_to? :strip! }
end
end
Use like this:
class MyModel < ActiveRecord::Base
include AttributeStripping
# ...
end
UPDATE (9/10/2013):
Revisiting this answer a couple of years later, I see how the winds have changed. There's a cleaner way to do it now. Create a module like this:
module AttributeStripper
def self.before_validation(model)
model.attributes.each_value { |v| v.strip! if v.respond_to? :strip! }
true
end
end
and set its method to be invoked at the right time in your model:
class MyModel < ActiveRecord::Base
before_validation AttributeStripper
# ...
end
This module is easier to test since it's not a mixin.
I have dealt with these sort of data integrity issues in various applications.
I used to manipulate the input like that.
But now, the best advice I have actually seen and followed is to store whatever the user types.
Then do post-processing on the backend to do the strip.
Create additional database fields (destripped) if you really want it in the database model table.
The main reason for this is one (primary) thing - when users want to revisit their data, i.e. edit, they're usually gonna expect to see what they typed in. A secondary reason is that you will avoid the possibility that your strip doesn't work right and either mangles the data or actually throw an error.
I've written a plugin for this purpose some time ago. I haven't tried it in a while and it doesn't have tests - so no guaranties that it still works. The upside would be a clean model:
class Story < ActiveRecord::Base
strip_strings :title, :abstract, :text
end

Resources