Finds in Rails 3 and ActiveRelation - ruby-on-rails

I'm trying to understand the new arel engine in Rails 3 and I've got a question.
I've got two models, User and Task
class User < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :user
end
here is my routes to imply the relation:
resources :users do
resources :tasks
end
and here is my Tasks controller:
class TasksController < ApplicationController
before_filter :load_user
def new
#task = #user.tasks.new
end
private
def load_user
#user = User.where(:id => params[:user_id])
end
end
Problem is, I get the following error when I try to invoke the new action:
NoMethodError: undefined method `tasks' for #<ActiveRecord::Relation:0x3dc2488>
I am sure my problem is with the new arel engine, does anybody understand what I'm doing wrong?
Sorry guys, here is my schema.db file:
ActiveRecord::Schema.define(:version => 20100525021007) do
create_table "tasks", :force => true do |t|
t.string "name"
t.integer "estimated_time"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
end
create_table "users", :force => true do |t|
t.string "email", :default => "", :null => false
t.string "encrypted_password", :limit => 128, :default => "", :null => false
t.string "password_salt", :default => "", :null => false
t.string "reset_password_token"
t.string "remember_token"
t.datetime "remember_created_at"
t.integer "sign_in_count", :default => 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.string "username"
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
add_index "users", ["username"], :name => "index_users_on_username", :unique => true
end

I believe you want:
def load_user
#user = User.where(:id => params[:user_id]).first
end
Until you ask for a record it will stay a relation.
But find(params[:user_id]) will still work and return the record.

Does it work if you change your load_user method as shown below?
def load_user
#user = User.find(params[:user_id])
end
Also, I think you might need to change your new action to:
def new
#task = #user.tasks.build
end

Don't confuse the Arel gem's interface with the new ActiveRecord query interface. The syntax described here will not work: http://github.com/brynary/arel
ActiveRecord uses Arel under the hood but creates its own Arel-like API. Here's a brief look at the new ActiveRecord query interface: http://m.onkey.org/2010/1/22/active-record-query-interface

It's actually pretty simple. Here is one method...
def new
#task = #user.tasks.new
end
private
def load_user
# you must call a non-deferred operator to actually return
# the tuple that is connected to your tasks
#user = User.where(:id => params[:user_id]).first
end
Be sure to take a look at the seven part learning series I am doing on Active Relation. The first episode will help you understand what your error was more clearly. http://Innovative-Studios.com/#pilot
find() IS NOT deprecated in some instances as stated before. I would limit the use of find() for atomic values (where you are searching for a specific item/index) Anything that could possibly be collection based I would stick to the Where (restriction) clause wrapper in ActiveRecord for Arel.

Related

Rails, issue selecting by boolean

I'm working through a Rails tutorial, and have hit a wall I'm not sure if I missed a step. I have two models, both with a boolean called 'visible', but I can only select using it on one of the models. For reference here are my migrations
class CreateSubjects < ActiveRecord::Migration
def up
create_table :subjects do |t|
t.string "name"
t.integer "position"
t.boolean "visible", :default => false
t.timestamps null: false
end
end
def down
drop_table :subjects
end
end
And
class CreatePages < ActiveRecord::Migration
def up
create_table :pages do |t|
t.integer "subject_id"
t.string "name"
t.string "permalink"
t.integer "position"
t.boolean "visible", :default => false
t.timestamps null: false
end
add_index("pages", "subject_id")
add_index("pages", "permalink")
end
def down
drop_table :pages
end
end
Both have a t.boolean "visible", :default => false in them. And I can edit them and change that value fine. But if I say pull up a rails console and try...
This works fine:
Subject.visible
This gives me a NoMethodError: undefined method `visible':
Page.visible
During one point in the tutorial I had to change the line
#page.sections.visible.sorted.each
to
#page.sections.where(:visible => true).sorted.each
Which did work, but I have no idea WHY I had to do so
Link to project on GitHub if it helps
https://github.com/TaylorHuston/Rails_LyndaCMS
The visible function is an instance function, so you have to use it on an instance of Page:
Page.new.visible
You were able to use it on Subject because you created a scope :visible, so you have one function for the instance and one for the Relation.

Getting a Twilio 404 Error Rails Receiving SMS

I'm trying to create an app that accepts an SMS message through Twilio, and then creates a check-in/out transaction that is tied to both employee models and item models. A simple SMS-based item checkout/checkin tracker. I have the twilio app wired up to listen on tooler.herokuapp.com/twilio/twilio_create, but when I send messages to the number, nothing happens and I get a 404 error within twilio's logs. Not sure exactly what's going on, was hoping someone might be able to help. In this case, I'm taking the FROM from twilio and putting it into employee_id, and the BODY from twilio and putting it into item_id. Why won't it create new transactions?
db/schema.rb
ActiveRecord::Schema.define(:version => 20130516162824) do
create_table "employees", :force => true do |t|
t.string "phone"
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "items", :force => true do |t|
t.string "description"
t.string "assettag"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "transactions", :force => true do |t|
t.boolean "status"
t.integer "item_id"
t.integer "employee_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "transactions", ["employee_id"], :name => "index_transactions_on_employee_id"
add_index "transactions", ["item_id"], :name => "index_transactions_on_item_id"
create_table "users", :force => true do |t|
t.string "email", :default => "", :null => false
t.string "encrypted_password", :default => "", :null => false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", :default => 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
end
app/controllers/twilio_controller.rb
class TwilioController < ApplicationController
def process_sms
#city = params[:FromCity].capitalize
#state = params[:FromState]
render 'process_sms.xml.erb', :content_type => 'text/xml'
end
def twilio_create
#transaction = Transaction.new(:item_id => params[:Body], :employee_id => params[:From])
#transaction.save
end
end
app/views/twilio/twilio_create.xml.erb
<Response>
<Sms>Received. You checked out <%= #body %>, <%= #from %> you lucky bastard.</Sms>
</Response>
I already got it working with the process_sms page, so I know that it's something with the twilio_create function. What am I doing wrong?
Should the URL be tooler.herokuapp.com/twilio/twilio_create.xml? You can check rake routes to see all URLs that conforms to your config/routes.rb.
Actually, Rails already has CRUD convention. Since you are creating a twilio resource, your config/routes.rb should be:
# config/routes.rb
resources :twilio do
collection do
get :process_sms
end
end
In the controller, you should use def create instead of def twilio_create.
class TwilioController < ApplicationController
def process_sms
#city = params[:FromCity].capitalize
#state = params[:FromState]
render 'process_sms.xml.erb', :content_type => 'text/xml'
end
def create
#transaction = Transaction.new(:item_id => params[:Body], :employee_id => params[:From])
#transaction.save
end
end
Lastly, rename app/views/twilio/twilio_create.xml.erb to app/views/twilio/create.xml.erb.
In order to create a new transaction, do a post request to tooler.herokuapp.com/twilio.xml. That URL will hit the def create in TwilioController and render app/views/twilio/create.xml.erb.
If it still doesn't work because of 404 error, you can check rake routes to see all URLs that conforms to your config/routes.rb.

Getting ActiveRecord::RecordNotUnique but then not able to find the existing record

I have something like the following code. (Model names changed as they're not important to what's happening.)
The #find_or_create_bar_for_blah_id works fine most of the time. Occasionally it will return nil though and I'm not sure why.
It's some kind of race condition as the problem happens in resque jobs that run as part of our app.
Any clues as to how #find_or_create_bar_for_blah_id could return nil?
class Foo < ActiveRecord::Base
has_many :bars, :dependent => :destroy
def find_or_create_bar_for_blah_id(locale_id)
begin
bars.where(:blah_id => blah_id).first || bars.create!(:blah_id => blah_id)
rescue ActiveRecord::RecordNotUnique
bars.where(:blah_id => blah_id).first
end
end
end
class Bar < ActiveRecord::Base
belongs_to :foo
validates_uniqueness_of :blah_id, :scope => :foo_id
end
# db/schema.rb
create_table "bars", :force => true do |t|
t.integer "foo_id"
t.integer "blah_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "bars", ["foo_id", "blah_id"], :name => "index_bars_on_foo_id_and_blah_id", :unique => true
add_index "bars", ["foo_id"], :name => "index_bars_on_foo_id"
create_table "foos", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "foos", ["name"], :name => "index_foos_on_name"
Doh! This was because we were using update_attribute in part of the code, which of course doesn't run AR validations. embarrassed face

Using association params in collections

I want to narrow down a collection using params accessible through association
class PostsController < ApplicationController
load_and_authorize_resource
def index
if params[:event_id] then
#event = Event.find(params[:event_id])
if params[:category_id] then
#category = Category.find(params[:category_id])
#posts = Post.where(:event_id => #event.id,
:product => {:category_id => #category.id })
end
end
end
Gives me the error
No attribute named 'category_id' exists for table 'product'
But the column 'category_id' does exist in the table 'product'. Searching for this error hasn't shown me anything helpful yet. I've also tried using 'delegate' to make the attribute accessible but that hasn't worked either. I'm stumped.
Here is the schema
create_table "posts", :force => true do |t|
t.float "quantity"
t.datetime "deadline"
t.integer "product_id"
t.integer "event_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "products", :force => true do |t|
t.string "title"
t.text "desc"
t.text "ingredients"
t.float "deposit"
t.float "cost"
t.string "units"
t.float "quantity"
t.float "deadline_hours"
t.boolean "presell_option"
t.integer "user_id"
t.integer "category_id"
t.integer "club_id"
t.datetime "created_at"
t.datetime "updated_at"
end
Edit:
When I correct ':product' to ':products' I get this related error
SQLite3::SQLException: no such column: products.category_id: SELECT "posts".* FROM "posts" WHERE (("products"."category_id" = 2 AND "posts"."event_id" = 10))
This puzzles me further, the schema says I do have the category_id in the products table
You have to use the attribute name products instead of product. This is one one Rails exceptions to the rule.
#posts = Post.joins(:product).where(:event_id => #event.id,
:products => {:category_id => #category.id })
Try
#posts = Post.where(:event_id => #event.id,
:products => {:category_id => #category.id })
You can reduce your code and make your life easier by using MetaSearch gem. It's very nice tool! Video tutorial: here. Documentation: here.

Rails has_one and belongs_to help

I have two models: User and Store
class Store < ActiveRecord::Base
belongs_to :user
class User < ActiveRecord::Base
has_one :store
Schema looks like this:
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string "encrypted_password"
t.string "salt"
t.boolean "admin", :default => false
t.string "username"
t.string "billing_id"
end
create_table "stores", :force => true do |t|
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string "store_name"
t.integer "user_id"
end
User must login in order to sign up a store by inputting "email" and "store_name". create from stores_controller looks like this:
def create
#store = Store.new(params[:store])
if #store.save
#store.user_id = current_user.id
flash[:success] = "this store has been created"
redirect_to #store
else
#title = "store sign up"
render 'new'
end
end
In ApplicationsController
def current_user
#current_user ||= user_from_remember_token
end
However, when I check in the database, #store.user_id = nil. For some reason, it's not able to put in current_user.id into #store.user_id. Anybody able to help in detecting why this might be? I thought I had associations correctly implemented. Thanks
This is happening because you're setting the #store.user_id AFTER saving it.
Ideally, you should be using the association builder for this:
def new
#store = #current_user.store.build(params[:store])
More information about these can be found in the "Association Basics" guide.

Resources