Reducing an if statement to generate a mongoid query - ruby-on-rails

I have the code below and I would like to know if it possible to reduce the code.
bank_accounts = self.client_bank_account_id.nil? ? self.client.bank_accounts : self.client.bank_accounts.where(_id: self.client_bank_account_id)
I only need the where call when client_bank_account_id is not nil.

Not exactly shorter, but I'd say its more readable:
bank_accounts = self.client.bank_accounts
bank_accounts = bank_accounts.where(_id: self.client_bank_account_id) unless self.client_bank_account_id.nil?

That depends, are you going for 'shortest code possible' code golf style or simply the nicest, possibly easier reading code.
I'd go with:
bank_accounts = client.bank_accounts
bank_accounts = bank_accounts.where(_id: client_bank_account_id) if client_bank_account_id
Notes:
You don't need 'self'.
nil checks are probably not the best way to go.
Lazy loading should stop there being two calls to the db here

Related

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.

how do I declare const variables in ruby/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.

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)

Arel: How to cleanly join multiple conditions with OR?

In my Rails app, I loop through an array to create a list of conditions that must be joined by OR. Below is the basic flow of how I currently do so.
conditions = nil
set.each do |value|
condition = value.to_condition
conditions = conditions ? conditions.or(condition) : condition
end
Obviously, it's not beautiful, but I still don't fully know my way around Arel. Does it offer any better way of OR-joining a set of dynamically-generated conditions?
This is a perfect fit for an inject which will give you a one-liner you can use within something else as well:
conditions = set.inject { |conds, cond| conds.or(cond) } which can even be written: set.inject(&:or) which is very nice.
There is also a useful plugin for this.
conditions_helper
It helps to generate complex conditions.
I think that's basically it. I'd initialize conditions to the base object to avoid the ternary:
scope = Article
set.each{|v| scope = scope.or(v.to_condition)}

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