Help with ruby and def respond_to?(method,*args, &block) in library - ruby-on-rails

I have a Gem that deals with images that get modified. I want to modify it to update the old image but, I need to be able to get the object id.
Right now it uses the following code:
def respond_to?(method,*args, &block)
puts ("++++METHOD #{method.to_s} ARGS #{args} BLOCK #{block}")
args.each do |value|
puts ("ARGS #{value}")
end
but I don't know how to get id from something I pass in nor do I know how to pass the id in, I've tried
#asset.image.s_245_245(:asset_id=>#asset.id) with no success. Args returns nothing. What am I doing wrong?
Update: I am currently reading http://www.simonecarletti.com/blog/2009/09/inside-ruby-on-rails-extract_options-from-arrays/
Update: This too returned blank.

Your question is very unclear. How is the method respond_to? related to your problem with object id?
I am guessing that in reality you wanted to override method_missing, because now you do not call respond_to?, or it is not shown in your examples.
If you have not defined such method, calling image.s_245_245 will trigger method_missing (in the image object) with the parameters you used for respond_to?.
There is a rule, which says that if you use method_missing to handle some calls, then you should also modify respond_to?, and make it returning true when asked for the methods handled by method_missing.
As for object ID, there are two possibilities:
Every object in ruby responds to .object_id (which returns an internal identifier of every object)
ActiveRecord objects respond to .id (which is a primary key in the database).
This is just a side-note, because I suppose that if you start experimenting with method_missing instead of respond_to? you will know which one you want.

Related

Why isn't the args parameter used in ActionController::Instrumentation::render?

I am new to Ruby and to Rails, and am trying to understand fully what I'm reading.
I am looking at some of the Rails source code, in this case action_controller/metal/instrumentation.rb.
def render(*args)
render_output = nil
self.view_runtime = cleanup_view_runtime do
Benchmark.ms { render_output = super }
end
render_output
end
I understand that *args is using the splat operator to collect the arguments together into an array. But after that, it stops making much sense to me.
I can't fathom why render_output is set to nil before being reassigned to equal super and then called with no arguments. I gather that some speedtest is being done, but coming from other languages I'd expect this to just be something more like Benchmark.ms(render_output) or perhaps Benchmark.start followed by render_output followed by Benchmark.end. I'm having a hard time following the way it works here.
But more importantly, I don't really follow why args isn't used again. Why bother defining a param that isn't used? And I mean, clearly it is getting used-- I just don't see how. There's some hidden mechanism here that I haven't learned about yet.
In this context, it is important to note how super works, because in some cases it passes implicitly arguments and you might not expect that.
When you have method like
def method(argument)
super
end
then super is calling the overridden implementation of method implicitly with the exact same arguments as the current method was called. That means in this example super will actually call super(argument).
Of course, you can still define a method call that explicitly sends other arguments to the original implementation, like in this example:
def method(argument)
super(argument + 1)
end
Another important edge-case is when you want to explicitly call super without any arguments although the current method was called with arguments then you need to be very explicit like this
def method(argument)
super() # note the empty parentheses
end
Let me try to describe you what I think this code does.
*args*
is using the splat operator to collect the arguments together into an array
that is totally correct, however they don't use it, and if you will go to master branch, they just changed it to *. Asking why it is defined and not used, I think that's question about bad design. They should have called it _args or at least like it is now just single splat *.
render_output is set to nil because of scopes, it has to be explicitly defined out block, lambda, proc in order to store value in it, otherwise its visibility will be locked only to those lambda, proc, block execution. Refer to this article
Benchmark.start. Blocks are great ruby construction. You are totally correct that speedtest is done, we can see it is just decorator for benchmark library.
source.
You are wondering why we cannot just pass it as Benchmark.ms(render_output), that's because what will be given to benchmark ms function? It will be given result, like <div> my html </div. And how we can measure this string result - no how. That's why we calling super right in this block, we want to access parent class function and wrap it inside block, so we are not calling it, we just construct it, and it will be called inside benchmark lib, and measured execution like
class Benchmark
...
def realtime # :yield:
r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
yield
Process.clock_gettime(Process::CLOCK_MONOTONIC) - r0
end
...
end
So here we can count realtime of function execution, this is the code from original library

Ruby error - Undefined Method

I am try to write a function that will find the items in an array which match the string passed to the function. See code below.
class Island
def filter(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list = ["sasha","rory","rob","anthony","andre","tariq","kimberly","antoinette"]
list.filter("an"){|i| puts i}</i>
How i keep getting "undefined method 'filer' for #
I'm not sure what i'm doing wrong.
First let me object against the solution posted by #Sravan :
While it is true - and sometimes even a good solution - to monkey-patch a class, you have to be careful how to do it, because it may become a time bomb:
Ruby evolves, and new versions often add methods to existing classes. This means that if you add a method Array#search, and a new version of Ruby will also add a method of the same name, your new method will SILENTLY override the one in Ruby. You likely won't notice it for long time, until you are using a feature which is supposed to use Rubys Array#search - maybe by using something new in stdlib - and you get weird results. To track down this type of error can be a nightmare. This is exactly the case when you use search as a method name.
Now, how to do it then? Three possibilities:
(1) If you monkey-patch, use at least a method name which is unlikely to become part of the official interface. It might have your project's name as a prefix, or plenty of underscore characters, and so on. Note that this is not 100% foolproof: A later version of Ruby might add under the hood a private method with exactly the same name than the one you were choosing, but of course the odder your name, the less likely this will happen.
(2) If you don't like this idea of using "clumsy" names, you could at least test before defining the new method, whether it already exists, and throw an exception if it doesn't:
class Array
if self.method_defined?(:search)
raise "#{self.class}::search already defined"
else
def search(...)
...
end
end
end
(3) The third possibility is to avoid monkey-patching and keep the method in your Island class. In this case, the method definition would be different:
class Island
def self.filter(array, string)
...
end
end
and it would be called by
Island.filter(myarray, mystring)
UPDATE: Forgot a forth possibility:
(4) You can make Island a subclass of Array. I don't know what else you want to do with your islands, but maybe this is an option worth considering:
class Island < Array
def filter(string)
...
end
end
Of course, when invoking filter, you need to turn your array into an island, before you can use it:
list = Island.new([....])
Following ruby's convention over configuration, you can add/overwrite any method in any class
So, adding a function to array class makes it accessible to all the arrays. So, in this solution.
1) First thing is you have taken the filter function in Island class, instead, you need to take inside Array class since the list is an array.
class Array
def filter(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list = ["sasha","rory","rob","anthony","andre","tariq","kimberly","antoinette"]
list.filter("an"){|i| puts i}
O/P:
anthony
andre
antoinette
2) Since Filter is a keyword as suggested by other answer, take another name for it. Eg: search
class Array
def search(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list.search("an"){|i| puts i}

Some questions about symbol and instance method in Ruby

I'm currently try to learn ruby on 'Learn Ruby The Hard Way'
Here's my question...
The following code are from exercise 40:
cities = {'CA'=> 'San Francisco', 'MI'=> 'Detroit', 'FL'=> 'Jacksonville'}
cities['NY'] = 'New York'
cities['OR'] = 'Portland'
def find_city(map, state)
if map.include? state
return map[state]
else
return 'Not found.'
end
end
cities[:find] = method(:find_city)
while true
print 'State? (ENTER to quit) '
state = gets.chomp
break if state.empty?
puts cities[:find].call(cities, state)
end
I played around with the code, and finally understand how it works.
But I still don't understand about two things:
first...
In about middle of the code,
it defined a variable
cities[:find] = method(:find_city)
As what I know for now, the :(colon) declare a symbol.
I want to know is it a better practice to name a variable as cities[:find]
instead of using cities_find in this case?
I'm not quite sure what's the differences, or maybe it's much readable for most rubyist?
And the second one is also about the same line.
method(:find_city)
I know it allows me to call the find_city method.
But again, why I have to put a colon before find_city?
Does this code means parse the arguments I put in to symbols?
I have to say that Learn Ruby The Hard Way gives us a really Really REALLY GOOD example of what we should NOT do. No rubyist will ever type such code in his/her projects. This piece of code is confusing, unreadable and is an abuse of metaprogramming.
Anyway, I dissect that code for you.
The confusing part starts with this line:
cities[:find] = method(:find_city)
Let's look at the right side of the =. It calls a method whose name is method, as you may guess, the return value of the method call is the method find_city, more precisely, a Method object that wraps the method find_city with its scope. Then that method is stored in the hash cities, with a symbol :find as the key. So the value of cities now become
{
'CA'=> 'San Francisco',
'MI'=> 'Detroit',
'FL'=> 'Jacksonville',
'NY' => 'New York',
'OR' => 'Portland',
:find => #<Method:main.find_city>
}
You can see that the last key-value pair is really really weird, and it shouldn't be there because the hash cities should only store states and their capitals. Heck!
Then here comes this even weirder expression cities[:find].call(cities, state). Let's see how this work.
cities[:find] simply retrieve the Method object from the hash (still remember what method it wraps?)
cities[:find].call(cities, state) invokes the method it wraps, which is find_city, in the scope that the Method object wraps, which is the top level object (a.k.a. main), Method#call returns whatever the method wrapped in returns. So this expression is just find_city(cities, state), written in an alien style.
cities[:find] = method(:find_city)
Here, cities is a hash and the method object returned by method(:find_city) is assigned to the hash key find which is a symbol.
I think it depends upon you and the context of program where you are writing this.
A simple method_var = method(:find_city) would work here as well.
method(:find_city)
I know it allows me to call the find_city method. But again, why I have to put a colon before find_city? Does this code means parse the arguments I put in to symbols?
Here, you are passing the method name as an argument, you have to either pass it as a symbol or string.
In Ruby, the method method creates a Method Object. This allow you to pass it around in your code and call it later using the .call method on your Method object.
Since calling method(my_method) would evaluate my_method and pass the result to method(...), you need a way to tell the method method which method to use. That's why you basically pass in the method name as a Symbol into the method method :D
So it actually defined a proc, and make :find and find_city sort like key and value...Probably...

Rails: find_or_create_by equivalent that will not persist until save

I'm currently using this code block to retrieve a user or create a new one:
user = User.find_or_create_by!(email:params[:email])
However, I don't necessarily want to persist the new user to the database.
Is there something like "find_or_new_by" that will only persist upon:
user.save
You seem to be looking for #find_or_initialize_by. Instead of calling create, the named method calls new. To save the object (assuming it is initialized, and not found), you will call save as you want.
And if you look at the source code for the method, it does exactly what you are looking for!
def find_or_initialize_by(attributes, &block)
find_by(attributes) || new(attributes, &block)
end
On the docs page I provided with the method link, I was not able to find a bang (!) version of the method. Using the bang will raise an error. Depending on your use, you may not need it at all.
try this
user = User.where(email: params[:email]).first_or_initialize
it will just initialize it if does not exists.

Can I access the collection an instance method was called on in ruby on rails

I'm working on implementing a search form in a ruby on rails application. The general idea is to use form_tag to submit the search fields (via params) to a search function in the model of the class I'm trying to search. The search function will then iterate through each of the params and execute a scoping function if the name of the function appears in params.
The issue is that when I call the search on a collection like so:
#calendar.reservations.search({:search_email => "test"})
I don't know how to refer to the collection of #calendar.reservations from within the search function.
Additionally I'm confused as to why #calendar.reservations.search(...) works, but Reservations.all.search gives me an error saying you can't call an instance method on an array.
I've got the details of the search method over here: https://gist.github.com/783964
Any help would be greatly appreciated!
I don't know how to refer to the
collection of #calendar.reservations
from within the search function.
If you use self (or Reservation, it's the same object) inside the classmethod, you will access the records with the current scope, so in your case you will see only the reservations of a particular calendar.
[edit] I looked at you search function, and I think what you want is:
def self.search(search_fields)
search_fields.inject(self) do |scope, (key, value)|
scope.send(key, value)
end
end
Additionally I'm confused as to why
#calendar.reservations.search(...)
works, but Reservations.all.search
gives me an error saying you can't
call an instance method on an array.
#calendar.reservations does not return a standard array but a (lazy) AssociationCollection, where you can still apply scopes (and classmethods as your filter). On the other hand Reservation.all returns a plain array, so you cannot execute search there (or any scope, for that matter).
You don't really need a search method at all, as far as I can tell.
Simply use where:
#calendar.reservations.where(:search_email => 'test')
I would strongly encourage you to look at the MetaSearch GEM by Ernie Miller. It handles the kind of thing you're working on very elegantly and is quite easy to implement. I suspect that your view code would almost accomplish what the GEM needs already, and this would take care of all your model searching needs very nicely.
Take a look and see if it will solve your problem. Good luck!
Reservation.all.search doesn't work because it returns all the results as an array, while Reservation.where(..) returns an ActiveRecord object (AREL). Reservation.all actually fetches the results instead of just building the query further, which methods like where, limit etc do.

Resources