How do I correctly interpolate controller name and action - ruby-on-rails

I'm trying to write a method that dynamically assigns the controller name and action for all my controllers in application_controller, but I can't seem to interpolate the value correctly. If I hardcode what I want, it will render the correct page so it seems to only be the interpolation that's the problem.
This is what I have:
def authorized
render 'pages/unauthorized' unless Policy.new(current_user.role)."#{controller_name.singularize}_#{action_name}"
end
which returns
syntax error, unexpected tSTRING_BEG, expecting '(' ...Policy.new(current_user.role)."#{controller_name.singularize... ... ^
If I wrap the whole thing in ( ) then the syntax error goes away, but then I get a:
undefined method `call' for #<Policy:0x00007ff29ced4ae0 #role="user">
if I hardcode Policy.new(current_user.role).wiki_show then it correctly checks resolves. What am I doing wrong in the interpolation?

You could use Object#public_send:
public_send(string [, args...]) → obj
Invokes the method identified by symbol, passing it any arguments
specified. Unlike send, #public_send calls public methods only. When
the method is identified by a string, the string is converted to a
symbol.
It works like:
class Foo
def bar
'bar'
end
end
bar_method = 'bar'
p Foo.new.public_send(bar_method) # "bar"
So you could do
Policy.new(current_user.role).public_send("#{controller_name.singularize}_#{action_name}")

Related

Dropping array into HTTParty API call argument throws `args.ids.split is not a function` error. How do I fix this?

I have a function called value that returns this array below:
[1893, 1724, 257344, 353491, 301337, 305470, 348, 264660, 76341, 68726]
However for more clarity, here is the value function that returns the value array above: It's a module like ListInfoService below and has 4 arguments which are the arguments supplied in the values method below ValueSearchService::ValueSearch.new(params[:one], params['two'] = 1_000_000, params[:three], params[:four]).values
def value
params = set_params_if_not_added
value_search = ValueSearchService::ValueSearch.new(
params[:one],
params['two'] = 1_000_000,
params[:three],
params[:four]
).values
value_search['data']
end
I also have a function where the argument ids is supposed to take the result of value (i.e. comma separated numbers):
module ListInfoService
class ListInfo
include HTTParty
base_uri 'localhost:7000'
def initialize(ids, limit, offset)
#options = { query: { ids: ids, some_params: some_params, another_params: another_params } }
end
def movies
self.class.get('/list', #options)
end
end
end
The URL I interpreted to make the module function above is:
localhost:7000/list?ids=2,3,6&some_params=5&another_params=0
But when I call the function by dropping value result into the function call below, I get args.ids.split is not a function error:
list_info = ListInfoService::ListInfo.new(
value,
params[:some_params],
params[:another_params]
).movies
render json: list_info
So how do I make sure my ids argument takes a comma-separated list of number?
Note: Am using HTTParty, Rails5.1, Ruby 2.6
I think the problem is the array value that I supplied inside ListInfoService::ListInfo that searches an api. If I can probably change how I call my api in the module or find a way to supply comma-separated list of ids needed, it will work.
Fixed. This is what I have done to make this work. Bear in mind this error is thrown as a result of passing an array into the HTTParty API call argument. Hence the args.ids.split is not a function Error.
Fixing this for me is just by converting the value to string instead of array by just adding .join(', ') to the response in the method. See below:
def value
params = set_params_if_not_added
value_search = ValueSearchService::ValueSearch.new(
params[:one],
params['two'] = 1_000_000,
params[:three],
params[:four]
).values
value_search['data'].join(', ') # This will solve it.
end
Thereby when you append value result to the defined HTTParty module call to the ids argument, it takes it as a string and it works fine.
list_info = ListInfoService::ListInfo.new(
value, # Note that this line is `ids` under ListInfoService::ListInfo argument and cant be array.
params[:some_params],
params[:another_params]).movies
render json: list_info

What is a `to_hash` in a rails app?

In my rails app, I see a lot of .to_hash. What exactly is it?
def to_hash
serializable_hash
end
When a method has keyword arguments, Ruby offers implicit conversion of a Hash argument into keyword arguments. This conversion is performed by calling to_hashon the last argument to that method, before assigning optional arguments. If to_hash returns an instance of Hash, the hash is taken as keyword arguments to that method.
Never implement implicit conversion methods unless you sure know what you are doing! It is widely seen, for example, the #to_hash method being implemented (maybe because of “prettier name” than #to_h?) and causing strangest effects.
def method(arg = 'arg', kw_arg: 'kw_arg')
[arg, kw_arg]
end
# As expected:
method() # => ['arg', 'kw_arg']
method(kw_arg: 'your keyword') # => ['arg', 'your keyword']
# Intended as nicety: implicit hash conversion
method({kw_arg: 'hash kw_arg'}) # => ['arg', 'hash kw_arg']
# But has bad side effects:
o = String.new('example object')
def o.to_hash # Now o responds to #to_hash
{ kw_arg: 'o.to_hash' }
end
method(o)
# => ['arg', 'o.to_hash']
# Ruby thinks that o is a Hash and converts it to keyword arguments -.-
method(o, o)
# => ['example object', 'o.to_hash']
# Same here, but since only the *last* argument is converted,
# the first is properly assigned to the first optional argument
Usually,do not define to_hash when you need it for explicit conversion to a Hash. Define to_h instead.
Refer Here

Rails access hash value

I'm playing around with Netflix's Workflowable gem. Right now I'm working on making a custom action where the user can choose choices.
I end up pulling {"id":1,"value":"High"} out with #options[:priority][:value]
What I want to do is get the id value of 1. Any idea how to pull that out? I tried #options[:priority][:value][:id] but that seems to through an error.
Here's what the action looks like/how I'm logging the value:
class Workflowable::Actions::UpdateStatusAction < Workflowable::Actions::Action
include ERB::Util
include Rails.application.routes.url_helpers
NAME="Update Status Action"
OPTIONS = {
:priority => {
:description=>"Enter priority to set result to",
:required=>true,
:type=>:choice,
:choices=>[{id: 1, value: "High"} ]
}
}
def run
Rails.logger.debug #options[:priority][:value]
end
end
Here's the error:
Error (3a7b2168-6f24-4837-9221-376b98e6e887): TypeError in ResultsController#flag
no implicit conversion of Symbol into Integer
Here's what #options[:priority] looks like:
{"description"=>"Enter priority to set result to", "required"=>true, "type"=>:choice, "choices"=>[{"id"=>1, "value"=>"High"}], "value"=>"{\"id\":1,\"value\":\"High\"}", "user_specified"=>true}
#options[:priority]["value"] looks to be a strong containing json, not a hash. This is why you get an error when using [:id] (this method doesn't accept symbols) and why ["id"] returns the string "id".
You'll need to parse it first, for example with JSON.parse, at which point you'll have a hash which you should be able to access as normal. By default the keys will be strings so you'll need
JSON.parse(value)["id"]
I'm assuming the error is something like TypeError: no implicit conversion of Symbol into Integer
It looks like #options[:priority] is a hash with keys :id and :value. So you would want to use #options[:priority][:id] (lose the :value that returns the string).

Ruby syntax error, unexpected '=', expecting ')'

I am attempting to write my own solution to a Ruby exercise from Rubymonk where the purpose is to create three methods (add, subtract, and calculate) so when 'calculate' is called you can determine whether or not numbers are added or subtracted based on what is passed in. I am receiving the following error:
main:11: syntax error, unexpected '=', expecting ')' def calculate(*numbers, options={})
Can anyone tell me what the issue is with my code? Thanks for any and all help!
def add(*numbers)
numbers.inject(0) {|sum, number| sum + number}
end
def subtract(*numbers)
numbers.inject{|diff, number| diff - number}
end
def calculate(*numbers, options={})
result = add(numbers) if options.empty?
result = add(numbers) if options[:add]
result = subtract(numbers) if options[:subtract]
result
end
def calculate(*numbers, options={})
is not a valid method definition b/c *numbers takes the place a variable number of arguments. You have two options as I see it -
def calculate(options={}, *numbers)
or
def calculate(*args)
numbers, options = args[0..-2], args[-1] || {}
if you want to keep the same argument order
The splat argument *numbers needs to be the last argument. Otherwise, how would Ruby know when to treat the last argument as options or as the last number?
You can use (*numbers, options) (without a default value), but that would require that you always pass an options hash to the method (otherwise your last number will be set as the options variable instead).
Try this way:
def calculate(options={},*numbers)
Using optional arguments after the fully optional argument ( the * notation) do not work since it creates an ambiguity.
Read more at:
http://www.skorks.com/2009/08/method-arguments-in-ruby/
You can't use both a splat and a param with a default as last argument, this is too ambiguous for the parser (how to know that the last arg passed is meant to be the options?)
you can work around this in many ways ; one idiom from rails (active support) is :
def calculate(*args)
options = args.extract_options!
# ...
end
where extract_options! is a monkey-patch to Array from ActiveSupport defined as follow :
def extract_options!
last.is_a?(::Hash) ? pop : {}
end
as a side note :
an options hash is not really usefull here. you could pass in just a symbol as first argument, maybe.
if you use a hash, logic could be simpler :
def calculate(*args)
options = args.extract_options!
method = options.fetch(:method, :add)
send method, *args
end
on add, you don't need inject(0), injectuses the first element of your array as a first "memo" value if you don't provide one
you can pass a symbol to inject, which will be the method called on your "memo" value, with "next value" as argument :
(1..10).inject(:+)
# this is the same as
(1..10).inject{ |memo, next| memo + next }
# or, more exactly
(1..10).inject{ |memo, next| memo.send :+, next }

How to get a list of the arguments a method is called with

How do I get a list of the arguments passed to a method, preferably one that I can iterate through?
For example something like
def foo(a,b,c)
puts args.inspect
end
foo(1,2,3)
=> [1,2,3]
?
Thanks!
You can always define a method that takes an arbitrary number of arguments:
def foo(*args)
puts args.inspect
end
This does exactly what you want, but only works on methods defined in such a manner.
The *args notation means "zero or more arguments" in this context. The opposite of this is the splat operator which expands them back into a list, useful for calling other methods.
As a note, the *-optional arguments must come last in the list of arguments.
If you define your method as you specified, you'll always have 3 args, or the method call is invalid. So "all the args" is already defined for you. So you would just change your method to:
def foo(a,b,c)
[a, b, c]
end
To define a method that can be called with any args (and to then access those args) you can do something like this:
def foo(*args)
args
end
What the * does is put all args after that point into an array.
As others pointed out you can use the splat operator (*) for achieving what you want. If you don't like that, you can use the fact that Ruby methods can take a hash as last argument with nicer syntax.
def foo(args)
raise ArgumentError if args.keys.any? { |arg| arg.nil? || !arg.kind_of?(Integer) }
end
puts foo(:a => 1, :b => 2, :c => "a") # raise an ArgumentError
To access the arguments inside the method you have to use args[:a] etc.

Resources