Using non-numerical identifier in Rails find function - ruby-on-rails

In many Rails examples I see
ModelName.find(:id) being used, supposed every model has a unique string attribute named :symbol, how can I set up the model so that find(:symbol) would work? Do I have to implement the searching algo myself?

You could use
ModelName.find_by_symbol("uniquestring")
More info on Rails Dynamic Finders

You might want to try "find_by_yoursymbol"
ModelName.find_by_yoursymbol("symbol value")
I just used this to check if a record exists and to create it if it isn't there.
Spree::MailMethod.create(:environment => "test") unless Spree::MailMethod.find_by_environment("test").present?

Related

What is the best possible way to avoid the sql injection?

I am using ruby 1.8.7 and rails 2.3.2
The following code is prone to sql injection
params[:id] = "1) OR 1=1--"
User.delete_all("id = #{params[:id]}")
My question is by doing the following will be the best solution to avoid sql injection or not. If not then what is the best way to do so?
User.delete_all("id = #{params[:id].to_i}")
What about:
User.where(id: params[:id]).delete_all
Ok sorry for Rails 2.x its:
User.delete_all(["id = ?", params[:id]])
Check doc
Btw, be sure you want to use delete_all instead of destroy_all, the former doesn't trigger callbacks.
You can use this also
User.delete(params[:id])
The other answers answer this well for Rails and it'll work fine if you follow their suggestions. In a more generic setting when you have to handle this yourself you can typically use a regular expression to extract a value that's in an expected format. This is really simple with an integer id. Think of it like this:
if params[:id] =~ /(\d+)/
safe_id = $1.to_i
# do something with safe_id now
end
That gets a little more complicated when you're handling strings and arbitrary data. If you have to handle such data then you can use the quoting methods available for the database adapters. In Rails this is ultimately rolled into a consistent interface:
safe_string = ActiveRecord::Base.connection.quote(unsafe_string)
For most database systems this will handle single quotes and backslashes in a special manner.
If you're outside of Rails you will have to use the quoting methods specific to your database adapter, but usage is quite similar.
The takeaway:
If your data has a particular format, enforce the format with a regular expression
Otherwise, use your database adapter's quoting function to make the data "safe" for use in a query
Rails will handle most of this for you if you properly use the various methods and "conditions"
Use the rails methods to pass your where options. You can always hardcode them, as in the example that you give, but the usual way would be something like:
User.where(:id => params[:id]).delete_all
User.where("id = ?", params[:id]).delete_all
User.where("id = :id", :id => params[:id]).delete_all
They are well tested and in case a new vulnerability is detected, an update will fix the problem and your code will not need to be changed.
By the way, if you just want to delete 1 record based on its id, what I would do is:
User.find(params[:id]).destroy

AcitveRecord where method that matches just one record

Program.where(name: "xxyyzz123") will return a collection, even if there's just one record that matches which forces me to do ugly things like:
puts Program.where(name: "xxyyzz123").first.age
or
puts Program.where(name: "xxyyzz123")[0].age
When I know for sure only one record will match, is there a shorter way to grab a property from that one record?
ActiveRecord's dynamic attribute-based finders (find_by_x) allow you to select the first record that matches in your database. For example:
Program.find_by_name('xxyyzz123')
will return the first record with name = 'xxyyzz123'
Note that these finders are 'mildly deprecated' in Rails 4. Using
Program.find_by(name: 'xxyyzz123")
achieves the same thing and may make it easier when needing to update to the next version of Rails if they ever remove the former's functionality.
See ActiveRecord::Base in the API for more.
Yes, you will have to access that with Program.where(name: "xxyyzz123").first.age, however, in Rails 3, it is usually recommended to do that type of query with: Program.find_by_name('xxyyzz123').age.
Rails 4 deprecates the above syntax and recommends you to use the following syntax for that:
Program.find_by(name: 'xxyyzz123')
If you have multiple conditions, then simply : Program.find_by(name: 'xxyyzz123', lang: 'ruby')
Behind the scene, it does the same tomfoolery - where clause and returns first object.

How to refer to the current record in a Rails query?

In a Rails controller I'm using a JSON request to an external database to return integers that are used to order a collection of records:
Model.order(JSON.parse(open("http://myapp.com/models/#{:id}")).read)['attribute'])
I want to make this dynamic to aid switching between environments. Something like:
Model.order(JSON.parse(open(model_url(model))).read)['attribute'])
This is not correct, and model_url(model) is returning an error:
undefined local variable or method 'model'
How do I refer to self in query?
There must be a more elegant solution than
...JSON.parse(open("#{root_url}/models/{:id}"))....
EDIT:
Lightswitch05's answer below does anser the question I asked. The query should reference params[:id] to get the url of the current record.
In fact, I have decided to move this JSON call into a virtual attribute on the model. This means I can simply call Model.order(:my_virtual_attribute). While this solution brings its own share of issues—I needed to make url_helpers available to the model—in the long run I think this will be a cleaner solution.
As per our discussion the problem is that model is not defined. Since all you are trying to do with model is get the url to it, all you really need is params[:id]. This will fix your error message:
Model.order(JSON.parse(open(model_url(params[:id]))).read)['attribute'])
where model has been replaced with params[:id]
Change your code:
Model.order(JSON.parse(open(model_url(model))).read)['attribute'])
to:
model_tableized = Model.to_s.tableize
model_url = "#{model_tableized}_url(#{model_tableized.chomp('s')})"
Model.order(JSON.parse(open(model_url).read)["attribute"])
I think that should work.
You need the name method.
If you're using the model, and not an instance of the model, it would be Model.name
If you're using an instance, it would be
#model = Model.first
#model.class.name

Check if record exists in Rails before creating

I am trying to search my database before I enter the record, by doing this:
Product.update_or_create_by_name_and_date_and_applicationURL_and_server_and_addi_servers(app_name, app_date,url_app,server_name,addi_servers)
the problem is that I get an undefined method exception!
Is there another way to search for the same record before entering one?
You should use two steps:
#Suggestion 1
obj = Product.find_or_create_by_...
#Suggestion 2
obj = Product.find_or_initialize_by_...
obj.update_attributes hash_here
Rereading, your question, I can't really understand what do you want to update if you try to find an object with known attributes. Anyway, you would just have to adapt my answer a little if some fields are for identifying and some for update.
I would define a function in your model: something like
Product.find_by_everything
where you write out all the parameters of the search, instead of using the the long naming method.
Then, if that returns nil, create the product. This doesn't seem to be a good use case of using the built in activerecord naming methods.

Rails: Getting column value from query

Seems like it should be able to look at a simple tutorial or find an aswer with a quick google, but I can't...
codes = PartnerCode.find_by_sql "SELECT * from partner_codes where product = 'SPANMEX' and isused = 'false' limit 1"
I want the column named code, I want just the value. Tried everything what that seems logical. Driving me nuts because everything I find shows an example without referencing the actual values returned
So what is the object returned? Array, hash, ActiveRecord? Thanks in advance.
For Rails 4+ (and a bit earlier I think), use pluck:
Partner.where(conditions).pluck :code
> ["code1", "code2", "code3"]
map is inefficient as it will select all columns first and also won't be able to optimise the query.
You need this one
Partner.where( conditions ).map(&:code)
is shorthand for
Partner.where( conditions ).map{|p| p.code}
PS
if you are often run into such case you will like this gem valium by ernie
it gives you pretty way to get values without instantiating activerecord object like
Partner.where( conditions ).value_of :code
UPDATED:
if you need access some attribute and after that update record
save instance first in some variable:
instance=Partner.where( conditions ).first
then you may access attributes like instance.code and update some attribute
instance.update_attribute || instance.update_attributes
check documentation at api.rubyonrails.org for details

Resources