Rails Undefined columns methods - ruby-on-rails-3

I have strange problem, i have table products with model
class Product < ActiveRecord::Base
attr_accessible :id, :category_id, :name, :barcode, :price, ...
but when i run rails c i have no access to attributes.
product = Product.where("barcode='B0000000008'")
Product Load (22.5ms) EXEC sp_executesql N'SELECT [products].* FROM [products] WHERE (barcode=''B0000000008'')'
=> [#<Product id: 8, category_id: 2, name: "Aplikovaná boj. umění (1 hodina)", barcode: "P0000000008", price: #<BigDecimal:362f9c8,'0.95E2',9(36)>, ... ]
>> product.name
=> "Product"
>> product.class
=> ActiveRecord::Relation
>> product.barcode
!! #<NoMethodError: undefined method `barcode' for #<ActiveRecord::Relation:0x00000003a354c8>>
>> product.id
!! #<NoMethodError: undefined method `id' for #<ActiveRecord::Relation:0x00000003a354c8>>
>> product.price
!! #<NoMethodError: undefined method `price' for #<ActiveRecord::Relation:0x00000003a354c8>>
but im able to run
>> product = Product.new
>> product.name = "xx"
=> "xx"
>> product.class
=> Product(id: integer, ...)
whats difference between Product class and ActiveRecord::Relation class ? How i can get Product class from where method ? Thank you

First of all, where returns an ActiveRecord Relation. In simple terms, it is just un-executed Active Record query. You can chain queries like 'order', another 'where', 'joins' etc to this and only when you want to access the records returned by this query will the query be evaluated.
So what you did was
ActiveRecord::Relation.barcode which naturally fails.
Anyway, just do product = Product.where("barcode='B0000000008'").first and you'll get your product object on which you can call barcode

Related

rails - find with condition in rails 4

I recently upgraded my rails to Rails 4.1.6.
This query used to work :
#user = User.find(:all, :conditions => { :name => 'batman' })
Now I get this error message:
Couldn't find all Users with 'id': (all, {:conditions=>{:name=>"batman"}}) (found 0 results, but was looking for 2)
When I check the logs I can see that rails is trying to do a completely different query :
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" IN ('all', '---
:conditions:
:name: batman
')
It looks like, it's trying to get all the users with the id "all" and "{:conditions=>{:name=>"batman"}}". Please help.
UPDATE:
My real question behind that was I want to get a specific user and add to it his cars, only the cars that are blue. For example this is my query, the user id is 20.
#user = User.joins(:cars).find(20, :cars => {:color => "blue"})
But I get this error:
Couldn't find all Users with 'id': (20, {:cars=>{:color=>"blue"}})
(found 41 results, but was looking for 2)
You should definitely read this ActiveRecord Query Interface quide
User.where(name: "batman")
Some others already pointed out: The query syntax changed. Try this:
#user = User.joins(:cars).where(:cars => { :color => "blue" }).find(20)
Note that this will raise an exception if that record is not found, to return an array empty instead call:
#user = User.joins(:cars).where(:id => 20, :cars => { :color => "blue" })
I suggest to read: http://guides.rubyonrails.org/active_record_querying.html
If you want to load the user even if he does not have any cars and than display only his blue cars, I would do it like this:
#user = User.find(20) # returns the user
#user.cars.where(:color => 'blue') # returns the user's blue cars (or an empty array)
The find method is deprecated in this version of Rails (see the reference).
Instead, you must use the where method.
In your case, you should write #user = User(:name => 'batman') or #user = User(name: 'batman')

sphinxql: syntax error, unexpected IDENT

Getting this error with sphinx 2
sphinxql: syntax error, unexpected IDENT, expecting CONST_INT or CONST_FLOAT or '-' near 'WI AND published = 1 AND sphinx_deleted = 0 LIMIT 0, 10; SHOW META'
index.html.erb
error is being thrown in the template at the line of a partial collection: #posts_by_state, but two other instances of the same partial are working great. The State sort is what is throwing it off.
posts_controller.rb
#posts_by_state = Post.search(params[:search], with: { state: current_user.state, published: true }, :page => params[:page], :per_page => 10)
post_index.rb
ThinkingSphinx::Index.define :post, :with => :active_record do
indexes :title, as: :post_title
indexes :desc, as: :description
indexes tags(:name), as: :tag_name
#indexes happening_on, sortable: true
#has author_id, published_at
has published_at
has last_touched
has state
has published
set_property:field_weights => {
:post_title => 5,
:description => 1,
:tag_name => 10
}
end
String attributes in Sphinx can only be used for sorting - not filtering, not grouping - and so your options to work around this are as follows:
Pull it out into an associated model (State or PostState, perhaps?), and then filter by the foreign key integer instead.
Store that value as a field instead, and use :conditions instead of :with.
Hack around it with CRC32 values.
I highly recommend the first of these options (I'd argue it's cleaner, accurate), but it's up to you.

rails OR query based on multiple checkbox selections

This seems like it should be a common problem but I'm having trouble finding an answer. Basically I want to have a form with 10 or so checkboxes which I'm creating with check_box_tag. When the form is submitted I want to generate a query that return all records that match ANY of the checked selections. So, the number of checked selections will vary.
So, for example, if I have
class Book < ActiveRecord::Base
belongs_to :author
end
I want to generate something like
Book.where("author_id = ? or author_id = ?", params[authors[0]], params[authors[1]]) if there are two boxes checked, etc.
Thanks for any insight.
Will this work for you?
Book.where(author_id: [array_of_author_ids])
You need to collect author_ids from params first
I recently had to do something similar, this is how I achieved this. It's pretty clever (at least I think so. :))
I created a query model that serializes the query column (text field) in JSON. I use a form to get the query data from the user with selection fields.
class BookQuery < ActiveRecord::Base
has_many :books
# loop through each foreign key of the Book table and create a hash with empty selection
def self.empty_query
q = {}
Book.column_names.each do |column_name|
next unless column_name.ends_with?("_id")
q.merge column_name => []
end
end
end
I'm using Author as an example below:
<%= form_for #book_query do |f| %>
<% for author in Author.all %>
<%= check_box_tag "book_query[query][author_ids][]", author.id, false%>
<%= author.name %>
<% end %>
<%= f.submit "Save Query" %>
<% end %>
When this form is submitted you ended up with parameters like this:
When the form is submitted it generates this parameter:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"XXXXXXXXXXX", "book_query"=>{"query"=>{"author_ids"=>["2", "3"]}}, "commit"=>"Save Query"}
Now in the BookQuery controller's create action you can just do what create function always does:
def create
#book_query = BookQuery.build(params[:book_query])
if #book_query.save
flash[:success] = "Book query successfully saved."
redirect_to ...
else
flash[:error] = "Failed to save book query."
render :new
end
end
But by default rails serializes the data in hash type:
1.9.3p194 :015 > pp BookQuery.find(9).query
BookQuery Load (0.7ms) SELECT "book_queries".* FROM "book_queries" WHERE "book_queries"."id" = $1 LIMIT 1 [["id", 9]]
"--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nauthor_ids:\n- '2'\n- '3'\n"
=> "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nauthor_ids:\n- '2'\n- '3'\n"
In BookQuery model, add the following:
serialize :query, JSON
But rail would change the IDs to string:
1.9.3p194 :018 > query = JSON.parse(BookQuery.find(10).query)
BookQuery Load (0.5ms) SELECT "book_queries".* FROM "book_queries" WHERE "book_queries"."id" = $1 LIMIT 1 [["id", 10]]
=> {"author_ids"=>["2", "3"]}
1.9.3p194 :019 > query["author_ids"]
=> ["2", "3"]
What I did then is override the attribute accessors in BookQuery model:
The below has to be done because the hash returns strings, not ids in integer.
def query=(query)
query.each_pair do |k, v|
if query[k].first.present?
query[k].map!(&:to_i)
else
query.except!(k)
end
end
write_attribute(:query, query)
end
# just want to avoid getting nil query's
def query
read_attribute(:query) || {}
end
To find book with this query, you can simply add this function to your Book model:
def self.find_by_book_query(book_query, options = {})
options[:conditions] = book_query.query
find(:all, options)
end
Now you get a customizable query string based on the model definition Book and everything works like the Rails way. :)

Rails 3 polymorphic_path - how to change the default route_key

I got a config with Cart and CartItem (belongs_to :cart) models.
What I want to do is to call polymorphic_path([#cart, #cart_item]) so that it uses cart_item_path, instead of cart_cart_item_path.
I know I can change the url generated by the route to /carts/:id/items/:id, but that's not what I'm interested in. Also, renaming CartItem to Item is not an option. I just want to use cart_item_path method throughout the app.
Thanks in advance for any tip on that!
Just to make my point clear:
>> app.polymorphic_path([cart, cart_item])
NoMethodError: undefined method `cart_cart_item_path' for #<ActionDispatch::Integration::Session:0x007fb543e19858>
So, to repeat my question, what can I do in order for polymorphic_path([cart,cart.item]) to look for cart_item_path and not cart_cart_item_path?
After going all the way down the call stack, I came up with this:
module Cart
class Cart < ActiveRecord::Base
end
class Item < ActiveRecord::Base
self.table_name = 'cart_items'
end
def self.use_relative_model_naming?
true
end
# use_relative_model_naming? for rails 3.1
def self._railtie
true
end
end
The relevant Rails code is ActiveModel::Naming#model_name and ActiveModel::Name#initialize.
Now I finally get:
>> cart.class
=> Cart::Cart(id: integer, created_at: datetime, updated_at: datetime)
>> cart_item.class
=> Cart::Item(id: integer, created_at: datetime, updated_at: datetime)
>> app.polymorphic_path([cart, cart_item])
=> "/carts/3/items/1"
>> app.send(:build_named_route_call, [cart, cart_item], :singular)
=> "cart_item_url"
I think the same could work for Cart instead of Cart::Cart, with use_relative_model_naming? on the Cart class level.
You can declare the resources like this in your routes file.
resources :carts do
resources :cart_items, :as => 'items'
end
Refer to this section of the rails guide

Model.find(:all, :conditions ...) on one field for one key-value from a hash?

I have a table of email messages like so:
create_table :emails do |t|
t.string :emailMessageId
t.datetime :date
t.string :subject
t.string :gmailMessageId
t.string :gmailThreadId
t.string :from_hash, :default => nil
t.text :to_hash, :default => nil
t.text :cc_hash, :default => nil
t.integer :contact_id
The email.rb model file says:
class Email < ActiveRecord::Base
serialize :from_hash, Hash
serialize :to_hash, Array
serialize :cc_hash, Array
end
Imagine that
:to_hash = {"name" => "john", "email" => "john#test.com"}
or an array of hashes
:to_hash = [ {"name" => "john", "email" => "john#test.com"}, {"name" => "bob", "email" => "bob#example.com"} ]
As an example, here is Email.first
#<Email id: 1, emailMessageId: "357", date: "2011-10-03 00:39:00", subject: nil,
gmailMessageId: nil, gmailThreadId: nil, from_hash: {"name"=>"melanie",
"email"=>"mel#test.com"}, to_hash: [{"name"=>"michie", "email"=>"mich#blah.com"},
{"name"=>"clarisa", "email"=>"clarisa#123.com"}], cc_hash: [{"name"=>"john",
"email"=>"john#test.com"}, {"name"=>"alex", "email"=>"alex#massimo.com"}], contact_id: 1,
created_at: "2011-10-03 00:39:00", updated_at: "2011-10-03 00:39:00">
Further imagine that my database has thousands of such records, and I want to pull all records keyed on :to_hash["email"]. In other words, I want to be able to find all records in the Email model that contain the email "john#test.com" despite the fact that the email value is within an array of hashes. How do I do this?
I tried variations on:
hash = {"name" => "john", "email" => "john#test.com"}
Email.find(:all, :conditions => ["to_hash = ?", hash]) # returns the following error
ActiveRecord::StatementInvalid: SQLite3::SQLException: near ",": syntax error: SELECT "emails".* FROM "emails" WHERE (to_hash = '---
- name
- john
','---
- email
- john#test.com
')
I also tried:
email = "john#test.com"
Email.find(:all, :conditions => ["to_hash = ?", email])
# => [], which is not an error, but not what I want either!
And finally:
email = "john#test.com"
Email.find(:all, :conditions => ["to_hash['name'] = ?", email])
# which, as expected, gave me a syntax error...
ActiveRecord::StatementInvalid: SQLite3::SQLException: near "['name']": syntax error: SELECT
"emails".* FROM "emails" WHERE (to_hash['name'] = 'john#test.com')
The simple answer is;
if you need to query something, you shouldn't serialize it.
Saying that, I think the answer is just
Email.all(:conditions => ["to_hash LIKE '%email: ?%'", "john#test.com"])
If you look at the database contents this should satisfy you.
But going forward you should look for a better solution.
Serialization is great for storing structured data that you never need to use in a sql query,
but just gets in the way if you do.
If you really need this kind of freeform data structure, I suggest you look at using MongoDB and Mongoid.
However, within the usual Rails world, I'd suggest the following;
class Email
has_many :email_recipients
def to_hash
email_recipients.map do |recipient|
{"name" => recipient.name, "email" => recipient.email}
end
end
end
class EmailRecipient
# with columns
# email_id
# name
# email
belongs_to :email
end
One possible way to do this with just regular Ruby is to use the select method and let ActiveRecord take care of deserialization.
emale = "john#test.com"
Email.find(:all).select { |m| m[:to_hash]["email"] === emale }
Another possible solution is to serialize the search hash and match the serialized hash exactly how it is saved in the database. This requires that the hash has all attributes, not just the e-mail. Some useful links to the code that makes this happen available here. You'll see that ActiveRecord uses YAML for serialization by default, so something like this could work.
search_hash = {"name" => "john", "email" => "john#test.com"}
encoder = ActiveRecord::Coders::YAMLColumn.new(Hash)
search_string = encoder.dump(search_hash)
Email.find(:all, :conditions => ["to_hash = ?", search_string])