before_create outside of model - ruby-on-rails

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.

Related

Rails model call different class based on user flag

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

How to use a gem class as a model in rails?

I'm new to ruby and rails. I 've created a rails app that uses the acts_as_votable gem. The gem contains a class Vote that I'd like to use as a model for my app. Is it possible?
I have tried using rails g scaffold Vote. That way I got an empty Vote model and the appropriate controller and views. However, because acts_as_votable had already created a database table called votes /votes showed the pre-existing votes without their attributes. The same applies to votes/new, it didn't show any input fields as the new Vote class was empty.
I also thought about copying the gem class in my app but I knew it was a horrid idea.
As long as you have the gem in your Gemfile, you should be able to access the Vote class by specifying its namespace :
ActsAsVotable::Vote
So you should be able to use it as you use other models.
It is also possible to monkey-patch the class, for instance in an initializer :
ActsAsVotable::Vote.send( :include, MyModule )
Then in another file :
module MyModule
extend ActiveSupport::Concern
included do
# class eval any macro you want...
end
module ClassMethods
def foo
# define class methods...
end
end
def bar
# define instance methods...
end
end
However, i would advise against doing this. It is risky to build whole parts of your business logic on a class you don't own, and is not designed to be used directly ! As long as you just add minor features, it's okay, but if you really need custom behavior, just go ahead and re-implement an 'acts_as_votable` functionnality yourserlf.
It is not so difficult nor long, and you will own your logic, which would shield you from unpredictable changes in the Vote class when upgrading.
You can try to use it with ActsAsVotable::Vote, but i think it should be used in combination with an existing Model.

Refactoring validation methods and callbacks

I am using Ruby on Rails 3.0.7 and I have tree classes what behavior is almost the same (and also the code in them model files). All those have a name and a description attribute, run same validation methods and for both there is a before_save callback that maintains data consistent providing the same functions.
I would like to refactor validation methods and callbacks in a separated class\model (I think I have to locate them related files in the \lib folder of my application).
What I have to do to make that? What code I have to add in my classes and what in the refactoring class\model?
Well, you could just make a super class from which your three models inherit. I tend to put the abstract base class in app/models alongside the models themselves.
# app/models/thing.rb
class Thing < ActiveRecord::Base
# common code goes here, such as
before_save ...
validates_length_of :foo
end
# app/models/red_thing.rb
class RedThing < Thing
# methods specific to RedThing go here
end
# app/models/blue_thing.rb
class BlueThing < Thing
# methods specific to BlueThing go here
end
If your Things have many differences such that it doesn't make sense to group them like this, you'd want to use a module instead, which is a only bit more complicated.
Rails guides has info here:
http://guides.rubyonrails.org/active_record_validations_callbacks.html#creating-custom-validation-methods

What Are The Advantages and Disadvantages of Putting ActiveRecord Methods into Modules?

What are the advantages and disadvantages of creating a module like:
module Section
def self.included(base)
base.class_eval do
has_many :books
end
end
def ensure_books
return false if books <= 0
end
end
...where ActiveRecord methods are used in the module instead of directly on the class(es) they belong to?
Should modules be used for methods like this?
The most obvious advantage is that you can take functionality that is shared and put it into a single place. This is just a general advantage of keeping your code organized and modularized (no pun intended) – and you should, of course, do that
Using Active Record methods does not make these Modules special in any way.
The most obvious disadvantage is that your code, as written, is a little more complex. You can't use validates_presence_of in a module directly because it does not inherit from ActiveRecord::Base. (Rails 3 is supposed to make it easier to selectively extend your own classes/modules with bits of ActiveRecord-Functionality
Instead, you need to call the Active-Record-Methods on your model class when your model is included:
module FooHelper
def self.included(other)
other.send(:has_many, :foos)
end
end
So the prime disadvantage is that your code gets a little harder to read.
If you are just breaking up a single class into separate parts and don't need to reuse the code somewhere else, you could use the concerned_with-pattern which works by reopening classes.
On the other hand, If you need more functionality, like configuration parameters for your extension, consider writing a plugin
This code can be shared by models (classes).

pros/cons of a ::Base class (rather than acts_as_foo)

Rather than an acts_as_foo syntax**, I'm toying with the idea of extending ActiveSensor::Base < ActiveRecord::Base and then extending this base class with all the errors/validations/class methods/whizbangs.
Proposed: #active_sensor.rb gem
module ActiveSensor
include ActiveRecord
autoload :VERSION, 'active_sensor/version'
autoload :ActiveSensorError, 'active_sensor/base'
autoload :Base, 'active_sensor/base'
autoload :Validations, 'active_sensor/validations'
end
Pros:
I think it looks cleaner.
Architecture and class build-up emulates ActiveRecord
I have a lot of subclassing going on... want to check whether a component is hardware using :is_a? method.
Cons:
holding an extra inheritance class layer in memory... never used independently
not very conventional (only seen 1 other rails plugin do this)
Any advice? Is creating a new ::Base class just dumb? If so, why?
** The acts_as_foo pattern is common for Rails gems. This class method is added to every AR object, which loads and extends class and instance methods when loading the class.
I think you've already answered yourself. Quite simply, if you extend Base then you add the functionality to every Active Record class in your system, whereas if you use acts_as_foo - you can add it only to the models that require it.
Obviously form this, it's better to use acts_as_foo if you only want the functionality for some of your models... but if you want it in every model, then feel free to extend Base.

Resources