Could I improve this method with duck typing? - ruby-on-rails

Hopefully I haven't misunderstood the meaning of "duck typing", but from what I've read, it means that I should write code based on how an object responds to methods rather than what type/class it is.
Here's the code:
def convert_hash(hash)
if hash.keys.all? { |k| k.is_a?(Integer) }
return hash
elsif hash.keys.all? { |k| k.is_a?(Property) }
new_hash = {}
hash.each_pair {|k,v| new_hash[k.id] = v}
return new_hash
else
raise "Custom attribute keys should be ID's or Property objects"
end
end
What I want is to make sure that I end up with a hash where the keys are an integer representing the ID of an ActiveRecord object. I don't particularly enjoy having to iterate through the hash keys twice with all? to determine if I need to grab the ID's out.
Of course, I'll accept any other suggestions to improve this code as well :)

How you write this method should depend on whether you expect an exception to be thrown during the course of normal program execution. If you want a readable exception message because an end-user might see it, then throwing one manually makes sense. Otherwise, I'd just do something like this:
def convert(hash)
new_hash = {}
hash.each_pair { |k,v| new_hash[ k.is_a?(Integer) ? k : k.id ] = v }
return new_hash
end
This will accomplish exactly the same thing, and you'll still get an exception if an array key doesn't have an id field. Even better, this uses a little more duck typing because now anything that has an id field will be acceptable, which is better than explicitly checking for something being a Property. This makes your code more flexible, especially when unit testing.
We still have an explicit check for integer objects, but this kind of occasional special case is usually acceptable, especially when checking for built-in data types.

Duck typing is really just a nuanced version of polymorphism. In a statically typed language like Java you'd have to create an explicit interface that told the compiler all of the methods that a particular variable can accept. With a dynamic language like Ruby the interfaces still exist in an abstract sense, they're just implicit.
The problem is the fact that you're accepting two different data structures into one method. The way to make duck typing work is to require that all the objects that get passed to your method obey the same contract (i.e. it's always a hash of Integers to [Foo] objects.) The process of converting a hash with Property keys into the correct structure should be the job of the client code. That can be done very easily with a simple wrapper class or a conversion function consisting of just the body of your elseif clause.
Bottom line it's up to the guy calling the method to make sure his parameters all quack the way your method expects them to quack. If they don't, he's the one who need's to figure out how to make his turkey quack like a duck, not you.

What I want is to make sure that I end up with a hash where the keys are an integer representing the ID of an ActiveRecord object.
You should probably check for that when you're creating/inserting into the hash. You could try something like this:
h = {}
def h.put obj
self[obj.id]=obj
end
or maybe
h = {}
def h.[]= key, value
raise "hell" unless key == value.id
super
end

Related

How to check if a variable has a map available in rails? In other words, if a variable is mappable?

I am trying to deal with a legacy method which accepts a variable and returns the variable.amount.to_s. But now with some changes, the variable can also be an active record relation.
I basically want to be able to do something like this:
def method(variable)
if variable has map?
variable.map { |v| v.amount.to_f }.reduce(:+)
variable.to_s
else
variable.amount.to_s
end
end
but I am unable to figure out what that if condition should be ?
You can test for the method directly with respond_to?.
if variable.respond_to?(:map)
Or you can check that it is Enumerable which provides a large suite of methods used for iteration, including map.
if variable.is_a?(Enumerable)
The advantage of Enumerable is it tells you more about the object, and you can be more sure that its map is the map you expect and not just some method that happens to be named map. The disadvantage is it will miss anything which is not Enumerable but does implement an applicable map; I can't think of a case where that should happen.

How to assign to Rails ActiveRecord conditionally?

Hey I wasn't quite sure what to call this but here's the deal.
I'm trying to only assign things to my database value if
There isn't a value in the database already, and
The value I'm assigning isn't blank.
The rudimentary version of this code is:
venue.address = venue_json['address'] if venue.address.blank? && !venue_json['address'].blank?
where venue is my ActiveRecord result.
This is what I have now (a little better). With the init_value in the Venue.rb class.
Venue.init_value(venue.address, venue_json['address'])
def self.init_value(record, value)
if record.blank? && !value.blank?
record = value
end
end
I'd like to get to this point, but really have no idea how.
venue.address.init_value(venue_json['address'])
especially since I'd like it it work with any attribute of the ActiveRecord class not just the address value.
Separating it into a method sounds like a good idea, but in this case it makes more sense to use an instance method rather than a class method.
def init_attribute(attribute, value)
self.update(attribute => value) if self.send(attribute).blank? && value.present?
end
venue.init_attribute(:address, venue_json['address'])
Some quick comments on the snippet above:
Using direct assignment won't persist the database value. You could go with something else like update or update_column. Or you can use assignment and then call #save on the object.
Whenever you need something not to be blank, you can use the more readable Object#present? which is part of ActiveSupport.
You'll need to call the method with the same name as the attribute on the database object. For this you'll want to use Object#send from Ruby.

Can Lua support case-insensitive method calls?

I'm using Lua as a data description language for my C++ app. I have a bunch of C++ classes bound to Lua using SLB 2.0. I have methods bound such as 'SetPos' or 'SetName'. I specify the position or name (for example) using a table with values keyed as 'pos' or 'name'. I want to be able to take the key, prepend 'set', and call the method, if it exists (it may not). Is that possible? If so, any suggestions?
I know I could make my bound methods lower case, but I'd rather keep them the same as the methods they're bound to (that may be my fallback though). I could try to build the method name based on my naming standards, but case insensitivity is less error prone.
I feel there should be a tricky piece of Lua that could solve this using metatables, but I haven't been able to work it out myself.
Any suggestions?
Thanks!
Case insensitivity is not really something Lua handles. All table lookups and local variable accesses are ultimately case sensitive string compares.
The best solution would be to just accept that you're dealing with a case sensitive system, just like C++, and deal with it.
However, if you really want to, you can do this. The simplest way would be to put every possible case permutation of a name in your function table. So your function table would have this:
["setname"] = theFunction,
["Setname"] = theFunction,
["sEtname"] = theFunction,
["SEtname"] = theFunction,
...
You can of course automate this with a function that takes each name in the table and replicates its data based on the case permutations.
A more involved but easier to use mechanism would be to use the __index and __newindex metamethods along with the empty table trick.
function CreateCaseInsensitiveTable()
local metatbl = {}
function metatbl.__index(table, key)
if(type(key) == "string") then
key = key:lower()
end
return rawget(table, key)
end
function metatbl.__newindex(table, key, value)
if(type(key) == "string") then
key = key:lower()
end
rawset(table, key, value)
end
local ret = {}
setmetatable(ret, metatbl)
return ret
end
Instead of creating a table with {}, you create the table with this function call. The table should otherwise function as normal (though obviously member access will be slightly slower).

Trouble on finding a class object in a array of classes

I am using Ruby on Rails 3.0.7 and I would like to understand how to handle the following code in order to retrieve a class objects with a specified id.
In my view file I have:
#records = Users.all # This returns an array (class)
In another file, a partial template, I would like to retrieve, for example, the user with id 1, but if I make this:
#records.find(1)
I get an enumerator (class) of all records:
<Enumerator: [<Users id: 1, ... ] >
How can I find the user with id 1 (or other ids) "a là Ruby on Rails Way"?
UPDATE
I use #records = Users.all in a view file because I aim to minimize calls to the database since I need to iterate almost over all records and check them existence. If I do for example:
some_hash.each { |key, value|
put User.find(value)
}
and I go in the log file, I will see a lot of database requests.
Even though this is probably quite slow, and I suspect there are some less than optimal designs in the app you're working on (not judging, we've all been there), Array#index seems to be what you're looking for:
#records[#records.index{|user| user.id == 1}]
Edit
Although if you need to do something for every user, and you need to access them by id quickly, I'd probably do something like this in your controller. Even if it's not really faster, it's much more readable (to me anyways):
#users_hash = {}
User.all.each{|user| #users_hash[user.id] = user}
Then in your views you can do:
#users_hash[id].username
Use User.scoped instead of User.all. #all will immediately query the database and return an array, whereas #scoped will return an ActiveRecord::Relation object which you can chain further queries. In this case, the database won't be hit until you try and somehow inspect or enumerate the result
Actually you're mistaken. #records.find(1) is returning an object of the class Enumerator (which is not the same as the class Enumerator itself).
The problem here is that, as you've noted, #records is an Array, not an ActiveRecord object, and Array#find (inherited from Enumerable#find--which, when not given a block, returns an object of class Enumerable) is not the same method as ActiveRecord::Base#find (i.e. User#find).
What you should do is, in your controller, pick out the one user record you want:
#user = User.find 1
...and then use #user directly in your template. Generally you should avoid doing ActiveRecord lookups (e.g. find) in your templates. That kind of logic should happen in your controller.
Last time for such case I ended up doing like this:
#assignments = Assignment.find_by_sql(' ... ')
#assignments.find(id: 1).first

Updating "it" inside a Groovy closure

I have a domain class that is just a list of strings (youtubeLinks).
When saving these links I want to strip out the video ID and save it instead of the entire URL entered on the UI side.
This is what I'm trying (ignore that the regex is flawed)
youtubeLinks.each {
def youtubeRegex = /v=(.*)/
def matcher = ( it =~ youtubeRegex )
it = matcher[0][1]
}
When I save this, it saves the original value of "it". Is there a way to update this reference and have it save properly?
Thanks.
Groovy's each loop is merely an iterator, and as such it neither affects the collection on which it operates, nor returns a value of its own. It's basically equivalent to Java's "advanced for loop (for-each)," only with the convenience of dynamic typing and an implicit loop variable (it). While it can be modified, it's a futile enterprise, as you'd be simply changing a reference to the original value, not the value itself. See this question for more on that.
When you need to modify every element within a collection somehow, the idiomatic Groovy (Grails) solution is to use the collect method. Collect transforms each element via the closure you provide, ultimately returning a new collection ( so, it doesn't actually "modify" anything).
Basically, you'll probably want to do something like this:
def links = '''http://www.youtube.com/watch?v=fl6s1x9j4QQ
http://www.youtube.com/watch?v=tCvMKcNJCAY
'''
assert (links =~ /watch\?v=(.*)/).collect{match -> match[1]} == ["fl6s1x9j4QQ", "tCvMKcNJCAY"]
..Though there are actually a number of ways one could go about such a task in Groovy.
Additionally, Ted Naleid's blog has some nice examples of Groovy pattern matching that you may find useful.
Edit
Here are several ways in which you could abbreviate the solution you submitted:
youtubeLinks = youtubeLinks.collect{link -> (link =~ /\?v=(.*)$/)[0][1]}
or
youtubeLinks = youtubeLinks.collect{link -> link.replaceAll(/^.*\?v=/, "") }
or this (Though it's a little contrived)
youtubeLinks = youtubeLinks.join('\n').replaceAll(/.*\?v=/, '').split()
You were right, it ended up being like
youtubeLinks = youtubeLinks.collect {
def youtubeRegex = /v=(.*)[&]/
def matcher = ( it =~ youtubeRegex )
return matcher[0][1]
}
Thanks, Northover.

Resources