I'm implementing authorization in an app according to this Railscasts episode.
In one of the instance methods, Ryan Bates is using the method .call and I don't understand what exactly it is doing. This is the code for the whole model. And this is the particular method:
def allow?(controller, action, resource = nil)
allowed = #allow_all || #allowed_actions[[controller.to_s, action.to_s]]
allowed && (allowed == true || resource && allowed.call(resource))
end
The resource argument is an instance object and the local variable allowed is supposed to be a boolean value.
call evaluates the proc or method receiver passing its arguments to it.
pr = ->a{a.map(&:upcase)}
pr.call(%w[hello world])
# => ["HELLO", "WORLD"]
m = "hello".method(:concat)
m.call(" world")
# => "hello world"
It is used to call back a piece of code that has been passed around as an object.
Related
I know it's a very short question. I understand "{ }" represents a loop.
and the new operator creates a new active record object.
What does this line do in rails? from where does lead come?
Proc.new{|lead| lead.lead_details.name}
It creates new Proc object. lead doest't come from anywhere in this example since this Proc doesn't get called. But you can call that, passing it as a block, for example.
leads = Lead.includes(:lead_details) # I assume it's an AR model, obviously
p = Proc.new { |lead| lead.lead_details.name }
names = leads.map(&p)
This way, lead comes from map method and represent single element of leads array-like object, it's equivalent to this:
leads.map { |lead| lead.lead_details.name }
You can also call this procedure 'by hand', passing argument explicitly, like this:
p.call(leads.first)
# => Whatever is leads.first.lead_details.name
You can even write your own method using it as block, for example:
def first_do(collection)
yield(collection.first)
end
first_do(leads, &p)
# => Whatever is leads.first.lead_details.name
I am running into an issue, where I need to check if a class exists. However, I am passing the class to a variable and trying to check it from there.
My issue is I need to pass the actual constant for defined?() to work, but I'm passing a variable, so instead of seeing a constant, it sees a method or variable.
obj is a rails model instance, for example, a specific User, or a specific Car.
def present(obj, presenter_class=nil, view_context=nil)
klass = presenter_class || "#{obj.class}Presenter".constantize
if defined?(klass) == 'constant' && klass.class == Class
klass.new(obj, view_context)
else
warn("#{self}: #{klass} is not a defined class, no presenter used")
obj
end
end
Pry Output:
[1] pry(ApplicationPresenter)> defined?(klass)
=> "local-variable"
I tried the below, but I get a method back...
[18] pry(ApplicationPresenter)> defined?("UserPresenter".constantize)
=> "method"
How can I fix this issue?
Well, apparently Object#defined? does not the thing that you hoped it would do.
tests whether or not expression refers to anything recognizable (literal object, local variable that has been initialized, method name visible from the current scope, etc.). The return value is nil if the expression cannot be resolved. Otherwise, the return value provides information about the expression.
Your goal looks like you are rebuilding what the draper gem is doing with .decorate... Don't forget that most of the gems are open source and you can use that for trying things on your own. See for example the decorator_class method from them
decorator_name = "#{prefix}Decorator"
decorator_name_constant = decorator_name.safe_constantize
return decorator_name_constant unless decorator_name_constant.nil?
They use the method safe_constantize and this apparently returns nil when the constant is not available.
2.6.5 :007 > class UserPresenter; end;
=> nil
2.6.5 :008 > 'UserPresenter'.safe_constantize
=> UserPresenter
2.6.5 :009 > 'ForgottenPresenter'.safe_constantize
=> nil
To me that looks exactly like what you need, and it also safer than using constantize
def present(obj, presenter_class=nil, view_context=nil)
klass = presenter_class || "#{obj.class}Presenter".safe_constantize
if klass != nil
klass.new(obj, view_context)
else
warn("#{self}: #{klass} is not a defined class, no presenter used")
obj
end
end
I have a list of methods who if any of them evaluated to true, I have to trigger an action on the model, in this case it's model audit.
So for example :
def a?
false # in reality this is some code
end
def b?
true # in reality this is some code
end
def c?
true # in reality this is some code
end
Now I can group this into like a parent method like :
def parent_method
a? || b? || c?
end
This will short-circuit the code, and c? will never be executed which is great. I can execute my .audit method.
But if I wanted to pass in custom message to my .audit method, and I wanted to have different message for every method how can I do that?
My first though to have a hash with keys being the methods and values being the messages or something along that line. But in that case the short circuiting doesn't work as all methods are evaluated in advance. How can I make this work better and more efficient/elegant?
Instead of true your method could return a trueish value like a symbol.
def a?
false # in reality this is some code
end
def b?
:b # in reality this is some code
end
def c?
:c # in reality this is some code
end
That you still allow to short-circuit the code in
def parent_method
a? || b? || c?
end
But now parent_method will not only return true or false but it would return a symbol that your allow returning a message that might be stored in a hash:
key = parent_method
audit(key) if key
You can always break these out into a simple array if you're not passing in any arguments. A minimal example looks like:
TESTS = [ :a?, :b?, :c? ]
def parent_method
failed = TESTS.find do |test|
!send(test)
end
if (failed)
# The failed variable contains the name of the method that failed
# to return `true`. Do whatever you need to do here.
errors << "Test #{failed} returned false!"
false
else
true
end
end
if I wanted to pass in custom message to my .audit method, and I wanted to have different message for every method how can I do that?
You could use a case expression:
case
when a? then audit(:a)
when b? then audit(:b)
when c? then audit(:c)
end
I am unfortunately running an application on Rails 2.3.18 and am seeing strange behavior with the params variable within the controllers. There are some areas of the app that (for some reason) assign params to itself or an empty hash by default.
params = (params || {})
Now the params variable is initialized to be the request parameters so it should evaluate to true in a logical expression. However, in this case params gets set to {}. Why exactly is this happening?
I don't have a Rails 2.3 app to play around with but params in a controller is actually method but saying params = ... creates a local variable that shadows the standard params method. For example, try this in irb:
def x
puts 'x is being called'
{ :where_is => 'pancakes house?' }
end
x = x || { }
and you'll see that x is { } and the x method never even gets called. The code that says:
params = (params || {})
is effectively doing this:
params = nil # Declare a local variable and initialize it to nil
params = params || { }
# -------^^^^^^ the local variable above, not the method
However, if you force the RHS params to be a method call:
params = params() || { }
# -------------^^
then it should call the method but you'll still have the local params variable on the LHS of the assignment.
I think you need to figure out why the code is doing params = params || { } and refactor it so that it isn't necessary. Are these controller methods trying to work when they're not actually in a controller? Are they depending on dodgy edge cases in older versions of Ruby? Was that code added cargo-cult style by someone copying mystery code from a blog they didn't understand?
Lets say we have two classes:
class Test
end
class Test2
def a
end
end
and say we have a variable var which can point to an object of these class or a Hash, so:
var = Test.new
or
var = Test1.new
or
var = {"a" => 1}
Now we need to ensure that we call 'a' on 'var' only if its allowed (i.e. it should not be called in case it points to Test).
So, if I do:
var.a
it gives an error in case var points to Test object. Is there any way we can prevent the error? (Any other solution apart from doing .is_a? test ?)
You could use respond_to? to check if you can call the method on the object:
var.a if var.respond_to? :a
Strange questions lead to nasty answers. But at least it's working:
def a(test)
(test.respond_to?(:a) ? test.a : nil) ||
(test.respond_to?('has_key?') && test.has_key?('a') ? test['a'] : nil)
end
You can rescue from error, and return a default value, something like this
result = var.a rescue ""
Here blank string is used as default for illustration purposes, you can pick a reasonable default for your case