how do I declare const variables in ruby/rails - ruby-on-rails

I would like to be a better code.. making my code cleaner and more readable.
One thing I've seen in c/c++ is the use of const on local variables. I think there is a lot of value in telling the reader of my code that once the variable is set, it is not changed in the function/method.
I'm wondering.... is there a way to indicate a local variable is const?
#for example
sql = "select * from table1 where status = #{iStatusId}"
connection.execute(sql)
sql will not change in this method. Can I mark it so?
Ok.. that's a bad example, but I think the point is made... I hope :)
EDIT:
I added a bit of dynamic to the sql.. the iStatusId bit is a parameter passed into the method.
EDIT2:
I did google this... all articles talk of magic number replacement. That's a no brainer and what I'd consider a MACRO. CONTANTS are easy... I'm looking for const.. they are very different things.

It would be easy enough to cook up the logic you are describing, by using something like this:
class Thing
def unchangeable_attribute
#unchangeable_attribute
end
def unchangeable_attribute=(value)
raise SomeError if #unchangeable_attribute
#unchangeable_attribute = value
end
end
I tend to agree with Dave Newton, though, as I have a hard time imagining a great use case for something like this...

What about the obj.freeze method defined on Ruby objects? From the docs:
Prevents further modifications to obj. A RuntimeError will be raised if modification is attempted. There is no way to unfreeze a frozen object.

Constants just begin with a capital letter, so the following will work just fine:
MY_CONSTANT = 1234
However, overwriting a constant is possible, although it will issue a warning.

Const's in ruby aren't strictly logical. As "all" variables are really just masked pointers to objects, casting them to the c paradigm of const doesn't make sense.
It would be equal to
const void * value = 'bla';
You could still change value[1].
I'd love to be proven wrong here.

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")

How to test a random uniq values with rspec

I have this code:
def self.generate_random_uniq_code
code = sprintf("%06d", SecureRandom.random_number(999999))
code = self.generate_random_uniq_code if self.where(code: code).count > 0
code
end
The goal is create random codes for a new register, the code can't exist already in the registers
I'm trying test this way, but when I mock the SecureRandom it always return the same value:
it "code is unique" do
old_code = Code.new
old_code.code = 111111
new_code = Code.new
expect(SecureRandom).to receive(:random_number) {old_code.code}
new_code.code = Code.generate_random_uniq_code
expect(new_code.code).to_not eq old_code.code
end
I was trying to find if there is a way to enable and disable the mock behavior, but I could not find it, I'm not sure I'm doing the test the right way, the code seems no work fine to me.
Any help is welcome, thanks!
TL;DR
Generally, unless you are actually testing a PRNG that you wrote, you're probably testing the wrong behavior. Consider what behavior you're actually trying to test, and examine your alternatives. In addition, a six-digit number doesn't really have enough of a key space to ensure real randomness for most purposes, so you may want to consider something more robust.
Some Alternatives
One should always test behavior, rather than implementation. Here are some alternatives to consider:
Use a UUID instead of a six-digit number. The UUID is statistically less likely to encounter collisions than your current solution.
Enforce uniqueness in your database column by adjusting the schema.
Using a Rails uniqueness validator in your model.
Use FactoryGirl sequences or lambdas to return values for your test.
Fix Your Spec
If you really insist on testing this piece of code, you should at least use the correct expectations. For example:
# This won't do anything useful, if it even runs.
expect(new_code.code).to_not old_code.code
Instead, you should check for equality, with something like this:
old_code = 111111
new_code = Code.generate_random_uniq_code
new_code.should_not eq old_code
Your code may be broken in other ways (e.g. the code variable in your method doesn't seem to be an instance or class variable) so I won't guarantee that the above will work, but it should at least point you in the right direction.

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: How to simplify ".select(:my_field).map(&:my_field)"?

To get the invoice numbers of selected jobs I do:
Job.where(...).map(&:invoice_number)
Since the invoice numbers is all I need, I thought to add select(:invoice_number) (I assume the SQL query becomes more efficient this way):
Job.where(...).select(:invoice_number).map(&:invoice_number)
Is there a better way to do the same ? (the .select(:invoice_number).map(&:invoice_number) part seems inefficient to me)
You could always use select_values. Something akin to:
Job.connection.select_values(Job.where(...).select(:invoice_number).to_sql)
This avoids the instantiation of ActiveRecord objects.
I know that this question is rather old, but in case anybody else checks this out, this can be achieved with pluck (http://apidock.com/rails/ActiveRecord/Calculations/pluck). As per http://edgeguides.rubyonrails.org/active_record_querying.html#pluck "pluck makes it possible to replace code like: Client.select(:id).map(&:id) with Client.pluck(:id)"
Would love to be proven wrong, but.. I don't think it's possible
Since all the active record methods are chainable, a method that returned an array of strings would break this. If such a method existed then it wouldn't be in AR, I can't think of where else to look..
You can use GROUP_CONCAT.
invoice_numbers = Job.where(...).select('group_concat(invoice_number) as invoice_numbers').
first.invoice_numbers.split(',').map(&:to_i)
This approach is too long and is not very obvious, but it will run faster than
Job.where(...).select(:invoice_number).map(&:invoice_number)

Is there a better alternative to this Ruby idiom?

I'm finding myself writing this bit of code in my controllers a lot:
params[:task][:completed_at] = Time.parse(params[:task][:completed_at]) if params[:task][:completed_at]
Don't get hung up on what I'm doing here specifically, because the reasons change every time; but there are many circumstances where I need to check for a value in params and change it before handing it off to create or update_attributes.
Repeating params[:task][:completed_at] three times feels very bad. Is there a better way to do this?
One way to shorten this slightly is:
if c = params[:task][:completed_at]
params[:task][:completed_at] = Time.parse(c)
end
Or, you might prefer this:
params[:task][:completed_at] &&= Time.parse(params[:task][:completed_at])
In the second case, the assignment will only happen if the left side is "truthy".
I suppose you could consider doing something like this.
Implement #to_time on String and NilClass, perhaps in a extensions.rb (as recommended in Ruby Best Practices, e.g.
require 'time'
class String
def to_time
Time.parse(self) # add error/exception handling to taste
end
end
class NilClass
def to_time
nil
end
end
Then you can just call params[:task][:created_at].to_time and the duplication is gone.
I'm not at all sure that this necessarily constitutes "best practice", but IMHO it meets the objective of the question...
I am not incredibly familiar with Ruby, but since it has Perl roots, there may be a construct that allows you to write it like this:
$_ = Time->parse($_) for params[:task][:completed_at] || ();
basically exploiting the for loop to create an alias to the variable, if it exists
maybe something like:
(params[:task][:completed_at] || ()).each { |i| i = Time.parse(i) }
edit:
I see that Ruby has an alias keyword. I am not familiar enough with it to give a Ruby example, but in Perl, the above could also be written:
local *_ = \$params[$task][$completed_at];
$_ = Time->parse($_) if defined;
which specifies that $_ will be an alias for $params[$task][$completed_at]
I tried playing around with it breifly in Ruby, but didn't see a way to alias an identifier, just global variables.

Resources