Problems using ActiveRecord model on existing table - ruby-on-rails

I've created a model for an existing table using the following generator command:
script/generate model Group
The table in question have a different name, so I've changed the model to account for it.
set_table_name 'demandegroupe'
Then I've fired up the console to look if everything was working.
>> Group.all
[#<Group login: "XXXXXX", ...>, ...]
But, to my surprise, using this model in a view throws out weird errors. I returned to the console to make sure I wasn't hallucinating and here's what happened:
>> Group.first
#<Group login: "XXXXXX", ...>
>> Group.first.login
NoMethodError: undefined method `generated_methods' for 50:Fixnum
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/attribute_methods.rb:247:in `method_missing'
from (irb):2
After that the first expression is not working anymore:
>> Group.first
NoMethodError: undefined method `column_names' for 50:Fixnum
All columns are either varchar or int, where's that 50:Fixnum type is coming from?
Thanks

Are you using Mysql? Group is a reserved sql keyword. It looks like Rails 2.3.5+ handles this for mysql, but it may be causing the problem you're seeing on other databases or other versions of Rails.

Finally, I made a huge mistake, or more precisely overlooked an important detail! It's a simple column name clash, I had review them for potential issues, but missed one buried in the 26 others. That malicious column was named class and once AR generated it's magic code after accessing any column it just replaced the vital class method without throwing any error.
I expected few name clashed as the column names seemed all written in French, so didn't took enough time and must have read "classe" or something like that. Beware the mighty name clash!
Thanks to ehsanul and dmajkic for making me review those column names a second time.

Related

how to solve NoMethodError efficiently (rails)?

I am new to rails and notice a very odd pattern. I thought some error messages in Django were obscenely cryptic, but building my second app in rails I notice my errors come up NoMethodError more than 90% of the time.
How do rails people tell the difference between all these errors with the same name?
What does NoMethodError mean at it's core? It seems like what you're calling in the template is misspelled, or you're accessing attributes that don't exist?
Where can this error happen? Is the only possible cause in the template/view (the html.erb file)? Or can a bad call in the controller and whatnot cause same error?
Also, what is the best debugger gem to alleviate these issues? Reading the full trace isn't too helpful for beginners at least for a while, I;d like a first hand account of what debugger someone uses instead of reading hype
Thank you kindly
NoMethodError means you are calling a method on an object, but that object doesn't provide such method.
This is a quite bad error in the sense that is reveals a poorly designed and tested application. It generally means your code is not behaving as you are expected.
The way to reduce such errors is:
Make sure that when you are writing the code you are taking care of the various edge cases that may happen, not just the correct path. In other words, you need to take care of validating what's going on and making sure that if something is not successful (e.g. the user is not supplying all the input requested) your application will handle the case gracefully.
Make sure you write automatic test cases that covers the behavior of each method in your codebase
Keep track of the errors. When an error occurs, write a test to reproduce the behavior, fix the code and check the test passes. That will reduce the risk of regression.
This is not a Rails specific error actually. I'll try to explain what's happening at its core.
Ruby is a language that functions through message passing. Objects communicate by sending messages to each other.
The message needs to be defined as a method on the object to respond to it. This can be directly defined on the object itself, the object's class, the object's class's parents/ancestors or through included modules.
class MyObject
def some_method
puts "Yay!"
end
end
> MyObject.new.some_method
Yay!
Objects can define method_missing to handle unexpected messages.
class MyObject
def method_missing(name, *args, &block)
puts name
end
end
> MyObject.new.some_undefined_method
some_undefined_method
Without the method_missing handler, the object will raise a NoMethodError
class MyObject
end
> MyObject.new.some_undefined_method
NoMethodError: undefined method 'some_undefined_method' for #<MyObject...>
So what does this look like in Rails?
$ rails generate model User name:string
Produces this
# models/user.rb
class User < ActiveRecord::Base
end
Which has the following methods implemented among others (by ActiveRecord)
def name
end
def name=(value)
end
When you do the following:
# create a new User object
> user = User.new
#<User ... >
# call the 'name=' method on the User object
> user.name = "name"
"name"
# call the 'name' method on the User object. Note that these are 2 different methods
> user.name
"name"
> user.some_undefined_method
NoMethodError: undefined method 'some_undefined_method' for #<User...>
You'll see the same results whether you're calling it in your console, your model, your controller or in the view as they're all running the same Ruby code.
ERB view templates are slightly different in that what you enter is only evaluated as Ruby code when it's between <% %> or <%= %>. Otherwise, it gets written out to the page as text.
How do rails people tell the difference between all these errors with
the same name?
We usually look at the stack trace that comes back with the response (in development mode) as well as looking in the logs. In a dev environment, I am running my server in a console where I can scroll through the request.
What does NoMethodError mean at it's core? It seems like what you're
calling in the template is misspelled, or you're accessing attributes
that don't exist?
Due to dynamic coupling nature of Ruby. When Ruby determines that an object doesn't have a method by the name of the one that was called. It looks for a method by the name of "method_missing" within that object. If it's not defined then the super one is called which has the default behaviour of raising an exception. Rails leverages this mechanism heavily in it's internal dispatching
Where can this error happen? Is the only possible cause in the
template/view (the html.erb file)? Or can a bad call in the controller
and whatnot cause same error?
This error can happen wherever you have Ruby code, It has nothing to do with rails
Also, what is the best debugger gem to alleviate these issues? Reading
the full trace isn't too helpful for beginners at least for a while,
I;d like a first hand account of what debugger someone uses instead of
reading hype
An Invaluable tool for debugging is the gem 'pry'. It has many useful plug-able tools that greatly simplify debugging

Seeds file - adding id column to existing data

My seeds file populated the countries table with a list of countries. But now it needs to be changed to hard-code the id (instead of rails generating the id column for me).
I added the id column and values as per below:
zmb: {id: 103,code: 'ZMB', name: Country.human_attribute_name(:zambia, default: 'Error!'), display_order: nil, create_user: user, update_user: user, eff_date: Time.now, exp_date: default_exp_date},
skn: {id: 104,code: 'SKN', name: Country.human_attribute_name(:st_kitts_and_nevis, default: 'Error!'), display_order: nil, create_user: user, update_user: user, eff_date: Time.now, exp_date: default_exp_date}
countries.each { |key, value| countries_for_later[key] = Country.find_or_initialize_by(id: value[:id]); countries_for_later[key].assign_attributes(value); countries_for_later[key].save!; }
Above it just a snippet. I have added an id: for every country.
But when I run db:seed I get the following error:
ActiveRecord::RecordInvalid: Validation failed: Code has already been taken
I am new to rails so I'm not sure what is causing this - is it because the ID column already exists in the database?
What I think is happening is you have existing data in your database ... let's say
[{id:1 , code: 'ABC'},
{id:2 , code: 'DEF'}]
Now you run your seed file which has {id: 3, 'DEF'} for example.
Because you are using find_or_initialize_by with id you are running into errors. Since you can potentially insert duplicates.
I recon you should just clear your data, but you can try doing find_or_initialize_by using code instead of id. That way you wont ever have a problem of trying to create a duplicate country code.
Country.find_or_initialize_by(code: value[:code])
I think you might run into problems with your ids, but you will have to test that. It's generally bad practice to do what you are doing. Whether they ids change or now should be irrelevant. Your seed file should reference the objects that are being created not ids.
Also make sure you aren't using any default_scopes ... this would affect how find_or_initialize_by works.
The error is about Code: Code has already been taken. You've a validation which says Code should be uniq. You can delete all Countries and load seeds again.
Run this in the rails console:
Country.delete_all
Then re-run the seed:
rake db:seed
Yes, it is due to duplicate entry. In that case run ModelName.delete_all in your rails console and then run rake db:seed again being in the current project directory. Hope this works.
ActiveRecord::RecordInvalid: Validation failed: Code has already been taken
is the default error message for the uniqueness validator for :code.
Running rake db:reset will definitely clear and reseed your database. Not sure about the hardcoded ids though.
Check this : Overriding id on create in ActiveRecord
you will have to disable protection with
save(false)
or
Country.create(attributes_for_country, without_protection: true)
I haven't tested this though, be careful with your validators.
Add the line for
countries_for_later[key].id = value[:id]
the problem is that you can't set :id => value[:id] to Country.new because id is a special attribute, and is automatically protected from mass-assignment
so it will be:
countries.each { |key, value|
countries_for_later[key] = Country.find_or_initialize_by(id: value[:id])
countries_for_later[key].assign_attributes(value)
countries_for_later[key].id = value[:id] if countries_for_later[key].new_record?
countries_for_later[key].save(false)
}
The ids data that you are using in your seeds file: does that have any meaning outside of Rails? Eg
zmb: {id: 103,code: 'ZMB',
is this some external data for Zambia, where 103 is it's ID in some internationally recognised table of country codes? (in my countries database, Zambia's "numcode" value is 894). If it is, then you should rename it to something else, and let Rails decide what the id field should be.
Generally, mucking about with the value of ID in rails is going to be a pain in the ass for you. I'd recommend not doing it. If you need to do tests on data, then use some other unique field (like 'code') to test whether associations etc have been set up, or whatever you want to do, and let Rails worry about what value to use for ID.

NoMethodError undefined method `fields' for nil:NilClass

Using: Rails 3.1.1
I am trying to create a search engine in my application that browses a large database (apprx 100 000 items) for string matches.
I am using the following:
fp = FeededProduct.where("name LIKE '%blue%' OR description LIKE '%blue%' OR category LIKE '%blue%'")
for the search query for "blue".
When I run the equivalent search phrase in MySQL it works fine but when I try to run this in rails it shows:
NoMethodError: undefined method `fields' for nil:NilClass: SELECT `feeded_products`.* FROM `feeded_products` WHERE (name LIKE '%blue%' OR description LIKE '%blue%' OR category LIKE '%blue%')
Clues and troubleshooting:
This happens only for large search results, I have not been able to distinguish a number but it crashes when it should have returned 920 results but it does NOT crash when returning 6 results!
My conclusion of the above is either that it cannot keep all the 920 results in the memory OR that there is some type of row that makes it crash and the more results, the more likely it is that it will contain that type of row. I am leaning more towards the first conclusion.
I cannot troubleshoot it very well because it crashes (with the same error code) when I try to run:
raise fp.inspect
It also crashes for:
fp.each do |prod|
begin
puts 'Do nothing'
rescue
puts 'Something crashed'
end
but it does NOT crash for:
raise fp.count.inspect
So, am I having a memory type of problem? What can I do to trouble shoot this further and/or solve the problem?
Using: Mac OS X 10.7.2. Lion
Database: InnoDB
Adapter: Mysql2 (don't know which version)
Stack:
ActiveRecord::StatementInvalid (NoMethodError: undefined method fields' for nil:NilClass: SELECT feeded_products`.* FROM feeded_products WHERE (name LIKE '%blue%' OR description LIKE '%blue%' OR category LIKE '%blue%')): app/controllers/search_results_controller.rb:190:in `show'
Edit 2012-03-06 Additional trouble shooting:
I tried with
fp2 = FeededProduct.limit(60000)
to create a really big array of hits and it worked fine. So I guess that rules out my guess that the fp variable cannot hold a certain amount of items.
The core of the problem seems to be that if I use the:
fp = FeededProduct.where("name LIKE '%blue%' OR description LIKE '%blue%' OR category LIKE '%blue%'")
I cannot use the fp-variable for anything afterwards without the application crashing.
Can you post the code at line 190 in search_results_controller.rb, and any others that possibly refers to the "fields" attribute in your show method? Also relevant parts of your model FeededProduct from app/models/feeded_product.rb
app/controllers/search_results_controller.rb:190:in `show'
It's not clear what fields refers to from the info you posted. It could be a typo, bad code or a migration that needs to be run.
Also note that
fp.each.do |prod|
is not syntactically correct. It should be:
fp.each do |prod|
What if you moved that fp logic into your model instead?
class Item < ActiveRecord::Base
...
def self.feeded_products(query)
self.where("name LIKE '%#{query}%' OR description LIKE '%#{query}%' OR category LIKE '%#{query}%'")
end
...
end
...and then in your controller, you can just call:
Item.feeded_products(query)
Hope this helps.
I changed to mysql instead of mysql2 adapter and it solved the problem. Thanks everyone for trying! I learned a lot trouble shooting your suggestions.
For these situations in which you have large sets of objects you can consider the use of find_each and find_in_batches methods or some kind of pagination.

set_table_name only works once?

I'm trying to use set_table_name to use one generic model on a couple different tables. However, it seems as though set_table name only works on the class once per application session. For instance in a rails 3 console (ruby 1.8.7) the following happens:
GenericModel.set_table_name "table_a"
puts GenericModel.table_name # prints table_a
pp GenericModel.column_names # prints the columns associated with table_a
GenericModel.set_table_name "table_b"
puts GenericModel.table_name # prints table_b
pp GenericModel.column_names # still prints the columns associated with table_a
Currently the workaround I've found is to also add .from(table_b) so that queries don't error out with 'table_b.id doesn't exist!' because the query still thinks it's FROM table_a.
Can others reproduce the issue? Is this the intended behaviour of set_table_name?
UPDATE
Adding
Model.reset_column_information
after set_table_name forces the model to work as I expect.
Reference found in http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000368
This is probably an undocumented limitation. Once the SHOW FIELDS FROM has been executed, which is where the results from column_names comes from, it is usually cached, at least for the duration of the request. If you must, try using the console reload! method to reset things.
your choice
rename_table
more info at AR TableDefinition

find_or_create_by_facebook_id method not found

I'm trying to find out where this function comes from. Any one have any clue? It's used by this:
http://github.com/fluidtickets/facebooker-authlogic-bridge
with a working example here:
http://facebooker-authlogic-bridge.heroku.com
Downloading the code, it throws: undefined method 'find_or_create_by_facebook_id' for #<Class:0xb04dd1c>
I have no clue where this function comes from.
Thanks all!
ActiveRecord creates dynamic finders based on columns in your database. So for example if you have a user with a username attribute then activerecord creates a number of dynamic finders:
find_by_username
find_or_initialize_by_username #=> calls new if not found
find_or_create_by_username #=> calls create if not found
You can string a few attributes together like
find_by_username_and_email
To get back to your problem - I suspect that you haven't run some required migration that adds the facebook_id to your users table in the db and therefore ActiveRecord isn't adding the dynamic finder for facebook_id to the class.

Resources