I am using Ruby on Rails 3.0.7 and I would like to know, regarding performance matters, what are differences between the User.find(<id>) method and the User.where(:id => <id>) method.
Under the hood, find does more or less what you're describing with your where. You can find the details in this post. That being said, if you're looking to grab a single record by id, then you might want to use find_one. That's what find winds up doing when you call it with a single argument of an id, but you'll skip past all the other code it needs to run to figure out that's what you wanted.
Short answer, but: It really doesn't matter (unless you don't have a unique-constraint on your id column).
Related
It's usal to see codes where a model where search in a foreign key is realized passing the whole object. For instance, search user exercises:
Exercise.where(user: user)
Another usual way to do that is:
Exercise.where(user_id: user.id)
Checking in console log, both querys search by the user id, but there is a "most recommended" way between both? If yes, why: more elegant, performatic?
I would choose the first one
Exercise.where(user: user)
It is mainly stylistic preference but also if the users every become polymorphic on exercises this would work without any changes whereas the second option would need to be changed to the first.
The benchmarks i quickly did showed no real performance difference.
Exercise.where('user_id = ?', user.id)
Not sure I understood your question correctly, but if you are looking for other ways to query or rather best practices then above is the right way to query in Rails.
The reason it is accepted as best practices is because its a Rails Security Countermeasure
Rails Guides: Array Conditions
Rails Security Guides: SQL Injections: Countermeasures
So to answer your question, yes there is good reasoning behind querying this way.
I am using Ruby on Rails 3.1 and I would like to know how, when and why I should use one of the following code rather than another on internationalizing my application (I18n gem):
t(:test_key)
t('test_key')
t('.test_key')
That is, what is the "subtle" difference between using t(:test_key), t('test_key') and t('.test_key')? What are best practices about this issue?
I think first two are equivalent and you just refer to main key in your translations, fo example
t('hello_world')
# t(:hello_world) is an equivalent
would reffer to
en:
hello_world: "Hello world"
However if you use dot notation, its called lazy lookup
and it will look deeper in your translation structure based on controller/action notation
So if you use this inside users/index template
t('.hello_world')
it will be resolved to
pl:
users:
index:
hello_world: "Witaj świecie"
You'll find more about internalization in Rails Guides
I guess it's a but up to you to decide when you actually want to use the different ones, but I'd prefer to use lazy lookup as much as possible in my views, unless you need to translate some generic component whose keys does not live in the scope of your view.
The reason why I prefer the lazy lookup is that it makes the code look cleaner, and as long as you're familiar with how the i18n gem works, you shouldn't have any trouble knowing where to look for the keys.
On the other hand, if you have such components, they should really live in a partial, a cell or something similar.
One thing worth mentioning abouth the non-lazy ones, are that you can provide them with a scope for where to look for the key in question. Again, it's up to you whether you like t('foo.bar.baz.test_key') or t(:test_key, :scope => 'foo.bar.baz').
It also takes a bunch of other options, but all of this is neatly documented in the rails guide, so I won't explain it further here.
rails console
u = User.find(9)
u.id = 7 # There is no other record with id 7
u.save
=> true
User.all
The id has not changed.
How to change the primary ID? Why does it prevent this action?
Working in Rails 3.0.7 and PostgreSQL.
EDIT:
Since there are good reasons not to do this, I'll explain why and hopefully it is a good reason.
We are doing Usability Testing on Staging, so I want it to look like the Production to make it easy for everyone. I don't want to create confusion or errors in the Usability Testing process by having some things in one order on Staging and in a different order on Production. I only changed one PK id on Staging DB.
Don't do this on your production DB!
I'm not sure as to why it prevents that action, but it does for sure prevent it.
You can bypass this using the update_all method on for the user.
User.where(id: 7).update_all(id: 9)
Though if possible, you really should avoid doing this.
For me worked:
User.find(9).update_column(:id, 7)
Could you elaborate what your use case is? Why do you want to change the ID? What are you really trying to accomplish with it?
Generally it's a bad idea to do this, and Rails won't let you do this easily because it will break your data integrity!
Here's Why:
When you're using a relational database (like PostgreSQL) underneath, you will have relationships between your models, which means that you will use the model's IDs as a reference in other related models... which means that if you change an entry's ID , all those references to that entry will go stale and corrupt your database..
I would strongly suggest to analyze your requirements again, and try to find another way to accomplish what you need to do (without changing IDs)
#Jason pointed out a very valid point. I totally agree with him. But, you might have your reasons and I suggest you re-consider what you're trying to do.
To answer your question:
ID columns are protected by default for mass assignment and cannot be set manually. But, you can override this behavior by defining a method:
def self.attributes_protected_by_default
[] # ["id", ..other]
end
This will allow you to assign id's manually.
Another method (although it is not pure Rails) is to create a new column, and populate it with your new IDs.
Then, using DB management software (not Rails), remove the Primary Key attribute from the id column, delete it, rename your recently added column to "id", and give it the Primary Key attributes. (If you cannot do that directly in your DB software, then set the properties Unique, Auto-Increment, etc.)
You can also move the column to the front (MySQL syntax):
ALTER TABLE table_name MODIFY COLUMN id int(11) FIRST;
But there is another thing I'd really like to say. It hasn't been as bad on this question as I've seen elsewhere, but folks: it's all well and good to tell people it's USUALLY not a good idea, but that isn't an answer to the question. Please refrain from saying "Don't do that" unless you already know the person's use-case.
In other forums I've been greatly frustrated by people saying "Why do you want to do that?" or "Don't do that", and then not answering the question. They didn't give me credit for already KNOWING that it isn't standard practice, and they ASSUMED I didn't already know that it was not an ordinary use-case.
People on this page haven't been that bad, and I'm not trying to pick on them. I'm just admonishing: please, check your own behavior before presuming to lecture. Somebody asked a question, and while warnings may be a good idea, it is probably safe to presume they have a REASON for wanting an answer.
End of rant.
The ID is typically generated by the database as an auto-incrementing PK. You can't (and really shouldn't need) to modify it.
So we run a code quality tool called reek once in a while as part of our project. The tool basically looks for code smells and reports them. Here, we observed that we get "Duplication" smell every time we try to access a key in params more than once (As if we are making a method-call twice with same parameters or we are duplicating an if condition etc). However, params is just a Hash, right? Other hashes don't get duplication smell when their keys are accessed more than once.
Why is this so? What are params exactly? Does it make sense to cache params in a local variable then use them? Will it help or its the same? Or is there something wrong with the tool? Help!
With the current version it's best to run Reek only on your app/models folder, because it raises false positives against views and controllers.
params is a kind of DTO (data transfer object) close to the system boundary, and so its characteristics should be different than regular code. But Reek doesn't know that (yet). I plan to improve Reek in the near future so that it plays better with Rails. For now though, your best bet is to restrict it to looking at app/models (and maybe app/helpers and lib).
params is a method call that does a #params ||= #request.params
It might be that it thinks params is a complicated method, so it wants you to try and cache it in a variable, but, dont think that would be worth it especially since it is memoized (based on my rack_process.rb from Rails 2.2)
params[:foo] is a method-call to Hash#[], so reek is correct. I'm not familiar with reek, so I can't tell why other Hash accesses don't get counted the same. Hash#[] should be fast enough that you don't need to store it in a local variable unless you're in a very performance critical part of your code.
The only difference between the params Hash and a regular Hash is that it uses with_indifferent_access, meaning you can access any key with a String or a Symbol.
I believe every time you call params, there is an initialization step which generates method calls, i suppose you can try creating a params and checking number of calls.
this could be blind guess. :-)
I'm new to Ruby and recently ran into an issue comparing to values when creating a Ruby on Rails application. In a controller I had the following statement that always returned false:
if (user.id != params[:id])
The problem was the user.id (which is an Active Record) is an integer and params[:id] is a string. It took me a while to figure this out and I finally changed it to:
if (user.id != params[:id].to_i)
Now the statement works as expected.
To avoid this error in the future is there a way to "compile" or get Ruby to warn you if you try to compare 2 different types? Some other issues I've ran into that I would like to "compile check" are:
Warn me if I create a variable but don't use it. To help check for typos in variable names.
Make sure a method exists in a Class so I can avoid method name typos and also to help refactoring, for example if I rename a method.
I'm currently using Ruby 1.8.6-27 RC2 with Rails 2.3.2 and RadRails IDE on Windows.
Test first, then code. If you write tests that cover all branches of your application, you get the assurance that your code both runs and produces correct results.
EDIT: I should point out that the ability to compare two types, not depend on method names until the last second, etc. are core features of Ruby.
You don't call a method so much as you send a message to an object. The object is then responsible for figuring out how to handle the method. In Rails this is used to access DB columns in ActiveRecord. There are no methods for the columns until a message with the column name is sent to the object.
Static typing in Ruby goes against the duck typing system. One can often get polymorphism for free without worrying about complex inheritance/interface schemes.
I suggest embracing these features and compensate for the uncertainty through testing
Ruby doesn't allow you to redefine the == operator for Object. In ruby 1.8 you can't, Ruby 1.9 was supposed to do but I haven't been able to get my script working for core classes. It works well for custom defined objects.
class Object
alias :equal_without_warning :==
def ==(object)
unless self.class == object.class
warn("Comparing `#{self.class}' with `#{object.class}'")
end
equal_without_warning(object)
end
end
Assuming I didn't do some stupid coding error, the answer is NO: you can't check whether you are comparing different type of objects.
Also, I would say you don't. Actually Ruby isn't designed to work in this way, this is more a java approach rather than Ruby style.
Ruby isn't supposed to be safe. It lets you compare any two objects, and that's where much of its power comes from. Rails wouldn't be possible without such dynamic design.
Even a compiled language such as Java or C won't stop you from doing == on two objects. As Ben said, it's best to test first. Inspect the structures you're working with. One way to get information about a Ruby object is to use:
puts object.class
In general, the best way (I know of) to avoid this type of issue for dynamic/scripting languages is to move "logic" to methods/commands and write unit tests for them. Basically, anything that can fail should be tested. The code on the page should be dumb logic... rather than display only those items that meet a certain criteria, it should display all items, and get that list of items from a method that only returns the ones that should be displayed.
Two things I'd suggest:
One: Read up on IRB (or script/console for rails). A common development practice in dynamic languages is to try out snippets of code inside a "live" interpreter (like IRB or the rails console). This practice goes back to the earliest dynamic languages like Smalltalk and Lisp. Ruby-debug is also really useful for troubleshooting problems and would have been a really easy way to figure out the error in your example.
Two: Read up on "Duck Typing". "Types" and variables work a little bit differently in Ruby than many folks expect them to. As I understand it, a variable like user.id doesn't have a "type". The value pointed to by user.id does have a type, but the variable itself doesn't. I believe that's part of why there's no tool that would have told you what your error was in advance of running the program. Comparing those two variables isn't an error because the variables don't have a type. user.id was pointing to an integer at that point in your program, but it would be perfectly legal to assign user.id to point to a string, at which point that comparison would have made a lot more sense. :-)
The best solution I found was a IDE that did on-the-fly syntax checking, such as RubyMine. I'm not sure if it would have solved my original problem but it has helped me find and fix several other syntax and compile errors. Thank you everyone for your suggestions.