Where to instantiate class in rails - ruby-on-rails

I need to use the aws-sdk-comprehend gem in my model Metric, but I'm not sure if it's ok (accordingly to rails convention) to instantiate outside of the class, e.g:
comprehend = Aws::Comprehend::Client.new
class Metric < ApplicationRecord
def key_phrases
# Use comprehend object here.
end
end
Is there a rule of thumb to in regard to this situation? I don't want to instantiate in the key_phrases because it will instantiate every time I call it.

The answer was already posted in the comments. Here a full example of how it might look in idiomatic Ruby:
class Metric < ApplicationRecord
def key_phrases
# Use comprehend object here.
comprehend_client
end
private
def comprehend_client
#comprehend_client ||= Aws::Comprehend::Client.new
end
end

Related

Determine in model that which controller is trigger the save action?

Developing rails app for both api and front end. so we have products controller for api and products controller for the front and Product model is one for both.
Like that
class Api::V1::ProductsController < ActionController::API
def create
#product.save
end
end
class ProductsController < ActionController::Base
def create
#product.save
render #product
end
end
class Product < ActiveRecord::Base
def weight=(value)
weight = convert_to_lb
super(weight)
end
end
Basically in product we have 'weight field' and this field is basically capture weight from the warehouse. it will be different unit for the user. so i'm going to save whatever weight is capture by unit, its lb,g or stone but it will convert to lb and store into database.
So i write the overide method for the conversation. but i want this override method should only call for front app only and not for the api. because api will always post weight in lb(its need to be convert in client side)
Can you guys anyone suggest the solution? what should i use or what should i do for this kind of scenario.suggest if its any other solution for that kind of situation as well. Thanks in advance.
It's better to keep Product model as simple as possible (Single-responsibility principle) and keep weight conversion outside.
I think it would be great to use Decorator pattern. Imagine class that works like this:
#product = ProductInKilogram.new(Product.find(params[:id]))
#product.update product_params
#product.weight # => kg weight here
So, you should use this new ProductInKilogram from Api::V1::ProductsController only.
You have options to implement that.
Inheritance
class ProductInKilogram < Product
def weight=(value)
weight = convert_to_lb
super(weight)
end
end
product = ProductInKilogram.find(1)
product.weight = 1
It's easy, but complexity of ProductInKilogram is high. For example you can't test such class in an isolation without database.
SimpleDelegator
class ProductInKilogram < SimpleDelegator
def weight=(value)
__getobj__.weight = convert_to_lb(value)
end
end
ProductInKilogram.new(Product.find(1))
Plain Ruby (My Favourite)
class ProductInKilogram
def initialize(obj)
#obj = obj
end
def weight=(value)
#obj.weight = convert_to_lb(value)
end
def weight
convert_to_kg #obj.weight
end
def save
#obj.save
end
# All other required methods
end
Looks a little bit verbose, but it is simple. It's quit easy to test such class, because it does nothing about persitance.
Links
Single-responsibility principle
Delegate gem
Decorator Pattern in Ruby

What is the difference between Employee.last and Employee.last.decorate in Rails?

I didn't find any answer for this question after google. Please can anyone explain the difference between these two.
There is draper gem.
It allows you to decorate objects.
The aim is to move some methods which are related to object's presentation (and not related to business logic) out of model to some place (decorator).
For example given
# app/models/employee.rb
class Employee < ActiveRecord::Base
end
and
# app/decorators/employee_decorator.rb
class EmployeeDecorator < Draper::Decorator
def foo
p 'method from decorator is called'
end
end
You can do the following
emp = Employee.new # would be Employee instance
emp.foo # would be error since Employee has no foo instance method
emp.decorate.foo # => would be 'method from decorator is called'
P.S.
Check out the gem docs. It's very well documented. Bet you will love it.

rails - instance model include module on variable condition

I need to know if I can include a module to an instantiated model.
What works today :
in the controller
#m = MyModel.create(params)
in the model
class Doc < ActiveRecord::Base
after_save :set_include
def set_include
if bool
self.class.send(:include, Module1)
else
self.class.send(:include, Module2)
end
end
end
and this works, but I'm afraid that self.class actually include the module for the class model an not the instantiated model
In this case, this will work.
The module methods are call after the object is saved.
But in many case, the controller will call some modules methods.
I thought of called the method set_include (up there) in a before_action of the controller.
But I really thinks that is not a good idea...
Any idea how I can really do that with in a good way ?
thanks !
Answer to your direct question is no. Your code only appears to be working and is actually not modifying instance of a class, but the class itself. So all instances of it will be getting this "benefit". Probably not what you wanted. Let me demonstrate with simple ruby example: https://repl.it/BnLO
What you can do instead is use extend with instance like: https://repl.it/BnLO/2
Applied to your code it would be:
class Doc < ActiveRecord::Base
after_save :set_include
def set_include
if bool
extend(Module1)
else
extend(Module2)
end
end
end
Also, self is not necessary. https://repl.it/BnLO/3
You need to use instance class (a.k.a eigenklass):
def set_include
singleton_class.instance_eval do
include bool ? Module1 : Module2
end
end
However the fact that you want to do this is suspicious and might lead to a disaster. So the question is: what are you really trying to achieve here - there surely is the better way of doing so.

Why do functions from my Rails plugin not work without specifically requiring?

I need some help with my plugin. I want to extend ActiveRecord::Base with a method that initializes another method that can be called in the controller.
It will look like this:
class Article < ActiveRecord::Base
robot_catch :title, :text
...
end
My attempt at extending the ActiveRecord::Base class with robot_catch method looks like following. The function will initialize the specified attributes (in this case :title and :text) in a variable and use class_eval to make the robot? function available for the user to call it in the controller:
module Plugin
module Base
extend ActiveSupport::Concern
module ClassMethods
def robot_catch(*attr)
##robot_params = attr
self.class_eval do
def robot?(params_hash)
# Input is the params hash, and this function
# will check if the some hashed attributes in this hash
# correspond to the attribute values as expected,
# and return true or false.
end
end
end
end
end
end
ActiveRecord::Base.send :include, Plugin::Base
So, in the controller, this could be done:
class ArticlesController < ApplicationController
...
def create
#article = Article.new(params[:article])
if #article.robot? params
# Do not save this in database, but render
# the page as if it would have succeeded
...
end
end
end
My question is whether if I am right that robot_catch is class method. This function is to be called inside a model, as shown above. I wonder if I am extending the ActiveRecord::Base the right way. The robot? function is an instance method without any doubt.
I am using Rails 3.2.22 and I installed this plugin as a gem in another project where I want to use this functionality.
Right now, it only works if I specifically require the gem in the model. However, I want it the functionality to be included as a part of ActiveRecord::Base without requiring it, otherwise I'd have to require it in every model I want to use it, not particularly DRY. Shouldn't the gem be automatically loaded into the project on Rails start-up?
EDIT: Maybe callbacks (http://api.rubyonrails.org/classes/ActiveSupport/Callbacks/ClassMethods.html) would be a solution to this problem, but I do not know how to use it. It seems a bit obscure.
First, I would suggest you make sure that none of the many many built in Rails validators meet your needs.
Then if that's the case, what you actually want is a custom validator.
Building a custom validator is not as simple as it might seem, the basic class you'll build will have this structure:
class SpecialValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
# Fill this with your validation logic
# Add to record.errors if validation fails
end
end
Then in your model:
class Article < ActiveRecord::Base
validates :title, :text, special: true
end
I would strongly suggest making sure what you want is not already built, chances are it is. Then use resources like this or ruby guides resources to continue going down the custom validator route.
Answer
I found out the solution myself. Bundler will not autoload dependencies from a gemspec that my project uses, so I had to require all third party gems in an engine.rb file in the lib/ directory of my app in order to load the gems. Now everything is working as it should.
Second: the robot_catch method is a class method.

How does Rails method call like "has_one" work?

I am PHP dev and at the moment I am learning Rails (3) and of course - Ruby. I don't want to believe in magic and so I try to understand as much as I can about things that happen "behind" Rails. What I found interesting are the method calls like has_one or belongs_to in ActiveRecord models.
I tried to reproduce that, and came with naive example:
# has_one_test_1.rb
module Foo
class Base
def self.has_one
puts 'Will it work?'
end
end
end
class Model2 < Foo::Base
has_one
end
Just running this file will output "Will it work?", as I expected.
While searching through rails source I found responsible function: def has_one(association_id, options = {}).
How could this be, because it is obviously an instance (?) and not a class method, it should not work.
After some researching I found an example that could be an answer:
# has_one_test_2.rb
module Foo
module Bar
module Baz
def has_one stuff
puts "I CAN HAS #{stuff}?"
end
end
def self.included mod
mod.extend(Baz)
end
end
class Base
include Bar
end
end
class Model < Foo::Base
has_one 'CHEEZBURGER'
end
Now running has_one_test_2.rb file will output I CAN HAS CHEEZBURGER. If I understood this well - first thing that happens is that Base class tries to include Bar module. On the time of this inclusion the self.included method is invoked, which extends Bar module with Baz module (and its instance has_one method). So in the essence has_one method is included (mixed?) into Base class. But still, I don't fully get it. Object#extend adds the method from module but still, I am not sure how to reproduce this behaviour using extend. So my questions are:
What exactly happened here. I mean, still don't know how has_one method become class method? Which part exactly caused it?
This possibility to make this method calls (which looks like configuration) is really cool. Is there an alternative or simpler way to achieve this?
You can extend and include a module.
extend adds the methods from the module as class methods
A simpler implementation of your example:
module Bar
def has_one stuff
puts "I CAN HAS #{stuff}?"
end
end
class Model
extend Bar
has_one 'CHEEZBURGER'
end
include adds the methods from the module as instance methods
class Model
include Bar
end
Model.new.has_one 'CHEEZBURGER'
Rails uses this to dynamically add methods to your class.
For example you could use define_method:
module Bar
def has_one stuff
define_method(stuff) do
puts "I CAN HAS #{stuff}?"
end
end
end
class Model
extend Bar
has_one 'CHEEZBURGER'
end
Model.new.CHEEZBURGER # => I CAN HAS CHEEZBURGER?
I commend you for refusing to believe in the magic. I highly recommend you get the Metaprogramming Ruby book. I just recently got it and it was triggering epiphanies left and right in mah brainz. It goes over many of these things that people commonly refer to as 'magic'. Once it covers them all, it goes over Active Record as an example to show you that you now understand the topics. Best of all, the book reads very easily: it's very digestible and short.
Yehuda went through some alternatives on way to Rails3: http://yehudakatz.com/2009/11/12/better-ruby-idioms/
Moreover, you can use a (usually heavily abused, but sometimes quite useful) method_missing concept:
class Foo
def method_missing(method, *arg)
# Here you are if was some method that wasn't defined before called to an object
puts "method = #{method.inspect}"
puts "args = #{arg.inspect}"
return nil
end
end
Foo.new.abracadabra(1, 2, 'a')
yields
method = :abracadabra
args = [1, 2, "a"]
Generally, this mechanism is quite often used as
def method_missing(method, *arg)
case method
when :has_one
# implement has_one method
when :has_many
# ...
else
raise NoMethodError.new
end
end

Resources