Shortcut for conditional variable manipulation in Ruby/Rails - ruby-on-rails

One of the parameters my API controller receives has a big key name and I need to convert it from string to integer, if it is present, before sending it to the model to be persisted. Usually I would do one of the following:
params[:really_big_key_name] = params[:really_big_key_name].to_i unless params[:really_big_key_name].blank?
or
params[:really_big_key_name] = params[:really_big_key_name].present? ? params[:really_big_key_name].to_i : nil
As you can see, the code line becomes big, with more than 80 characters, and I want to stick with Ruby best practices. Is there a shorter, more Ruby way to do the same? Maybe "in place" methods. Something like arrays do with bang methods. Unfortunately, to_i! does not exist for strings, which is exactly what I need.

You could do this:
params[:really_big_key_name] = params[:really_big_key_name].try(:to_i)

Related

Ruby symbol naming

I am currently reviewing a clients code base for the company I am interning for. The API is built with Ruby on Rails. I am new to Ruby, but have programmed in other languages, so looking through the code (and some tutorials), I have been able to understand most of what is going on, but there is a line that looks like the code below that is throwing me off. Why do the names of the symbols have equals in front? This feels illegal, but ruby is kinda cool. Thank you for your help.
delegate :name=, :description=, :tags=, to: :user
Another point: In Ruby, as a pattern you don't use the prefixes set or get, instead you use just the attribute name for the get and suffix = for the set. Example: setName and getName methods would be name= and name respectively.
Ruby allows you to use the setter method in these sintaxes: obj_instance.name = 'Name' (You can omit the () and use space before caracteres like =) or obj_instance.name=('Name').
A ruby symbol has very few restrictions concerning the characters it can have, and it's mainly linked to the "easy" representation we usually choose.
It's totally possible to have the following symbols:
:"hello/world"
:€
:"a+b"
It becomes a bit trickier when the symbol is used to represent a method, because we like the easy syntax that comes with it, but it's actually the same as sending the symbol using e.g. public_send:
foo.public_send(:"a+b")
This code will work if you define a method named :"a+b". Of course, you can't define it with the usual def, but it's still possible.
Now, some methods such as name= offer an additional semantic, so that the following are equivalent:
object.name = "Fubar"
object.send(:name=, "Fubar")

Placeholders in order method of ActiveRecord

How do I do this in rails?
User.order("abs(height-?)",val)
Seems like placeholders are not supported in order method, is there some lower level method I can use to process plceholders?
I think this might be one place where you should actually use string interpolation, but use whitelisting to ensure val does not contain anything weird
if User.column_names.include? val
User.order("abs(height - #{val})")
else
#some error handling
end

Read json serialised objects back from a file

I am aiming to serialise a set of objects into a file so as to create a backup. I have the start of that working, using a methods on the models (simplified here, assuming I have two ActiveRecords foo and bar):
def backup(file, foo, bar)
file.write(foo.to_json(root: true))
file.write(bar.to_json(root: true))
end
This gives me a file as I desire, in this case with two records:
{"foo":{"Account_id":1,"Name":"F","created_at":"2013-04-16T10:06:19Z","id":1,"updated_at":"2013-04-20T11:36:23Z"}}
{"bar":{"Account_id":1,"Name":"B","created_at":"2013-04-16T10:06:19Z","id":1,"updated_at":"2013-04-20T11:36:23Z"}}
At a later date I then want to read that backup in and reinstantiate those objects, probably then persisting them back to the database. My aim is to iterate through the file checking the type of each object, then instantiating the right object.
I have part of the logic, but not yet all of it, I haven't worked out how I determine the type of each serialised object before I instantiate it. The code I have for a restore is as follows:
def restore(file)
file.each_line do |line|
**<some magic that parses my line into objectType and objectHash>**
case objectType
when :foo
Foo.new.from_json(objectHash)
Foo.process
Foo.save!
when :bar
Bar.new.from_json(objectHash)
Bar.process
Bar.save!
end
end
end
What I'm looking for is the bit that goes in the "some magic" section. I can just write the code to parse the line directly to determine whether it's a foo or a bar, but I feel like there's probably some tricky Rails/Ruby way to do this that is automatic. Unfortunately, in this case Google is not being my friend. All I can see are pages that are focused on json in the web requests, but not parsing json back in this way. Is there something I'm missing, or should I just write the code to split the string directly and read the object type?
If I do write the code to split the string directly, I would write something along the lines of:
objectType = line[/^{"(\w*)"=>(.*)}/, 1]
objectHash = line[/{"(\w*)"=>(.*)}/, 2]
This is pretty ugly and I'm sure there's a better way (which I'm still looking into), but I'm not sure that this is even the right approach v's there being something that automatically looks at a json representation and knows from the root value what object to instantiate.
Lastly, the actual instantiation using from_json isn't working either, it isn't populating any of the fields on my ActiveRecord. It gives me nil parameters, so I think the parse syntax isn't right.
So, that makes three questions:
Is there a way to determine which object it is that I'm just missing, that is much cleaner?
If there isn't and I need to use a regexp, is there a syntax to get both bits of the line parsed in a single go, rather than my two lines with the same regexp?
The from_json syntax appears unhappy. Is there a syntax I'm missing here? (no longer a question - the code above is fixed, I was using as_json when it should have been to_json, although the documentation is rather unclear on that....)
(Note: edits over time to clarify my question, and because I've now got a regexp that works (didn't before), but still not sure it's very elegant.)
Further information - one of the problems here, as I dig into it further, is that the as_json isn't actually giving me json - what I have in the file is a hash, not json at all. Further, the values for created_at and lastupdated_at in the hash aren't quoted - so basically that's what's causing the parse on the way back in to fail. I've worked out that I should use to_json instead of as_json, although the documentation suggests that as_json should work.
I'm not sure I fully understand you're methodology, but I think using JSON.parse() would help.
There's some good information here http://mike.bailey.net.au/2011/02/json-with-ruby-and-rails/
This would help you translate the raw object back to a hash.
OK, so I think I've got something that works. I'm not convinced at all that it's elegant, but it gives me the result. I'll spend some time later trying to make it cleaner.
The code looks like this:
file.each_line do |line|
objectType = line[/^{"(\w*)":(.*)}/, 1]
objectJSON = line[/{"(\w*)":(.*)}/, 2]
objectHash = JSON.parse(objectJSON)
case objectType
when 'foo'
restoredFoo = Foo.new(objectHash.except('id', 'created_at', 'updated_at'))
restoredFoo.created_at = objectHash['created_at']
restoredFoo.updated_at = objectHash['updated_at']
restoredFoo.save!
end
when 'bar'
restoredBar = Bar.new(objectHash.except('id', 'created_at', 'updated_at'))
restoredBar.created_at = objectHash['created_at']
restoredBar.updated_at = objectHash['updated_at']
restoredBar.save!
end
end
Items of note:
I feel like there should be a way to create the object that isn't a JSON.parse, but rather would make use of the from_json method on the model. I'm not sure what the from_json is good for if it doesn't do this!!
I'm having fun with mass_assignment. I don't really want to use :without_protection => true, although this would be an option. My concern is that I do want the created_at and updated_at to be restored as they were, but I want a new id. I'm going to be doing this for a number of entities in my application, I didn't really want to end up replicating the attributes_protected in the code - it seems not very DRY
I'm still pretty sure my reg exp can give me both objectType and objectJSON in one call
But having said all that, it works, which is a good step forwards.

Rails: Convert string to variable (to store a value)

I have a parameter hash that contains different variable and name pairs such as:
param_hash = {"system_used"=>"metric", "person_height_feet"=>"5"}
I also have an object CalculationValidator that is not an ActiveRecord but a ActiveModel::Validations. The Object validates different types of input from forms. Thus it does not have a specific set of variables.
I want to create an Object to validate it like this:
validator = CalculationValidator.new()
validator.system_used = "metric"
validator.person_height_feet = 5
validator.valid?
my problem right now is that I really would not prefer to code each CalculationValidator manually but rather use the information in the Hash. The information is all there so what I would like to do is something like this, where MAKE_INTO_VARIABLE() is the functionality I am looking for.
validator = CalculationValidator.new()
param_hash.each do |param_pair|
["validator.", param_pair[0]].join.MAKE_INTO_VARIABLE() = param_pair[1]
# thus creating
# "validator.system_used".MAKE_INTO_VARIABLE() = "metric"
# while wanting: validator.system_used = "metric"
# ...and in the next loop
# "validator.person_height_feet".MAKE_INTO_VARIABLE() = 5
# while wanting: validator.person_height_feet = 5
end
validator.valid?
Problem:
Basically my problem is, how do I make the string "validator.person_height" into the variable validator.person_height that I can use to store the number 5?
Additionally, it is very important that the values of param_pair[1] are stored as their real formats (integer, string etc) since they will be validated.
I have tried .send() and instance_variable_set but I am not sure if they will do the trick.
Something like this might work for you:
param_hash.each do |param, val|
validator.instance_eval("def #{param}; ##{param} end")
validator.instance_variable_set("##{param}", val)
end
However, you might notice there's no casting or anything here. You'd need to communicate what type of value each is somehow, as it can't be assumed that "5" is supposed to be an integer, for example.
And of course I probably don't have to mention, eval'ing input that comes in from a form isn't exactly the safest thing in the world, so you'd have to think about how you want to handle this.
Have you looked at eval. As long as you can trust the inputs it should be ok to use.

Stripping the first character of a string

i have
string = "$575.00 "
string.to_f
// => 0.0
string = "575.00 "
string.to_f
// => 575.0
the value coming in is in this format and i need to insert into a database field that is decimal any suggestions
"$575.00 "
We did this so often we wrote an extension to String called cost_to_f:
class String
def cost_to_f
self.delete('$,').to_f
end
end
We store such extensions in config/initializers/extensions/string.rb.
You can then simply call:
"$5,425.55".cost_to_f #=> 5425.55
If you are using this method rarely, the best bet is to simply create a function, since adding functions to core classes is not exactly something I would recommend lightly:
def cost_to_f(string)
string.delete('$,').to_f
end
If you need it in more than one class, you can always put it in a module, then include that module wherever you need it.
One more tidbit. You mentioned that you need to process this string when it is being written to the database. With ActiveRecord, the best way to do this is:
class Item < ActiveRecord::Base
def price=(p)
p = p.cost_to_f if p.is_a?(String)
write_attribute(:price, p)
end
end
EDIT: Updated to use String#delete!
So many answers... i'll try to summarize all that are available now, before give own answer.
1. string.gsub(/[\$,]/, '')
string.gsub!(/^\$/, '')
2. string[1..-1]
3. string.slice(0) # => "ome string"
4. s/^.//
Why (g)sub and regexp Just for deleting a character? String#tr is faster and shorter. String#delete is even better.
Good, fast, simple. Power of reverse indexing.
Hm... looks like it returns "S". Because it is an alias to String#[]
Perl? /me is cheking question tags...
And my advice is:
What if you have not dollar, but yena? Or what if you don't even have anything before numbers?
So i'll prefer:
string[/\d.+/]
This will crop leading non-decimal symbols, that prevent to_f to work well.
P.S.: By the way. It's known, that float is bad practice for storing money amounts.
Use Float or Decimal for Accounting Application Dollar Amount?
You could try something like this.
string = string[1..-1] if string.match(/^\$/)
Or this.
string.gsub!(/^\$/, '')
Remember to put that backslash in your Regexp, it also means "end of string."
you can use regex for that:
s/^.//
As laways, this is PCRE syntax.
In Ruby, you can use the sub() method of the string class to replace the string:
result = string.sub(/^./,"")
This should work.
[EDIT]
Ok, someone asked what's the gsub() is for:
gsub() acts like sub() but with the /g modifier in PCRE (for global replacement):
s/a/b/
in PCRE is
string.sub(/a/, "b")
and
s/a/b/g
is
string.gsub(/a/, "b")
in Ruby
What I'd use (instead of regular expressions) is simply the built-in slice! method in the String class. For example,
s = "Some string"
s.slice!(0) # Deletes and returns the 0th character from the string.
s # => "ome string"
Documentation here.

Resources