Rails 4 - association not working correctly - ruby-on-rails-4

I created 2 models using a rails generator like this:
$ bin/rails g model manager name:string
and
$ bin/rails g model blog/post title:string manager:references
With that, I have 2 models files:
# app/models/manager.rb
class Manager < ActiveRecord::Base
has_many :blog_posts
end
and
# app/models/blog/post.rb
class Blog::Post < ActiveRecord::Base
belongs_to :manager
end
going to the rails console I can create a manager and a post like this:
$ manager1 = Manager.new
$ manager1.name = "John Doe"
$ manager1.save
$ post1 = Blog::Post.new
$ post1.title = "Hello World"
$ post1.manager = manager1
$ post1.save
And on console, if I do:
$ post1.manager.name
that works perfectly. Returning the Manager's name. But if I do:
$ manager1.blog_posts
I expected to get the list of manager's posts. But I am getting this error:
NameError: uninitialized constant Manager::BlogPost
The same happing when I try to do Many-to-Relationship between "Blog:Category (app/models/blog/category)" and "Blog::Post (app/models/blog/post.rb)"
UPDATE
#Pavan solved my first problem... And based in that solution, I tried to do the this:
# app/models/blog/post.rb
class Blog::Post < ActiveRecord::Base
belongs_to :manager
has_and_belongs_to_many :blog_categories, class_name: 'Blog::Category', foreign_key: 'blog_category_id'
end
and:
# app/models/blog/category.rb
class Blog::Category < ActiveRecord::Base
has_and_belongs_to_many :blog_posts, class_name: 'Blog::Post', foreign_key: 'blog_post_id'
end
And based on that http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association, I generate the following migration:
class CreateBlogCategoriesPosts < ActiveRecord::Migration
def change
create_table :blog_categories_posts, id: false do |t|
t.belongs_to :blog_post, index: true
t.belongs_to :blog_category, index: true
end
end
end
But when I try this on console:
$ post1 = Blog::Post.first
$ post1.blog_categories
I got this error:
2.2.2 :002 > p.blog_categories PG::UndefinedColumn: ERROR: column blog_categories_posts.category_id does not exist LINE 1:
...log_categories_posts" ON "blog_categories"."id" = "blog_cate...
^ : SELECT "blog_categories".* FROM "blog_categories" INNER JOIN
"blog_categories_posts" ON "blog_categories"."id" =
"blog_categories_posts"."category_id" WHERE
"blog_categories_posts"."blog_category_id" = $1
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column
blog_categories_posts.category_id does not exist LINE 1:
...log_categories_posts" ON "blog_categories"."id" = "blog_cate...
^ : SELECT "blog_categories".* FROM "blog_categories" INNER JOIN
"blog_categories_posts" ON "blog_categories"."id" =
"blog_categories_posts"."category_id" WHERE
"blog_categories_posts"."blog_category_id" = $1

NameError: uninitialized constant Manager::BlogPost
You have has_namy :blog_posts defined in manager.rb, so Rails will look for a classname BlogPost which isn't exist in your case.
You should explicitly specify the classname to tell Rails to use it.
# app/models/manager.rb
class Manager < ActiveRecord::Base
has_many :blog_posts, class_name: 'Blog::Post'
end

Related

undefined method `expr' for nil:NilClass after upgrading to rails 5.2

I have two following models
class Machine < ApplicationRecord
has_many :machine_part_changes
has_one :current_identifier_part_change, -> {
sql = <<-SQL
INNER JOIN "machine_parts"
ON "machine_parts"."id" = "machine_part_changes"."machine_part_id"
AND "machine_parts"."is_machine_external_identifier" = 1
SQL
installed.joins(sql)
},
class_name: 'MachinePartChange'
end
class MachinePartChange < ApplicationRecord
belongs_to :machine_part
belongs_to :machine, touch: true
end
in rails 4.2 I was able to do like
Machine.eager_load(:current_identifier_part_change).first
but now after I upgrade to rails 5.2 I am getting following error when I run tests.
Failure/Error: machine = Machine.eager_load(:current_identifier_part_change).find(create_machine.id)
NoMethodError:
undefined method `expr' for nil:NilClass
I do not have much knowledge of sql :-( and code is written some other developer what can I do to pass this test.

Rails 4 update_all syntax - Argument error

I'm getting the error:
Wrong number of arguments (2 for 1)
On my Task model when I defined my method to update all status of tasks. What is the correct syntax?
class Task < ActiveRecord::Base
belongs_to :user
def self.toggle(user, groups)
groups.each do |status, ids|
user.tasks.update_all({status: status.to_s}, {id: ids}) #=> error here
end
end
end
class GroupIdsByStatus
def self.group(options = {})
result = Hash.new {|h,k| h[k] = []}
options.reduce(result) do |buffer, (id, status)|
buffer[status.to_sym] << id
buffer
end
result
end
end
class TasksController < ApplicationController
def toggle
groups = GroupIdsByStatus.group(params[:tasks])
Task.toggle(current_user, groups)
redirect_to tasks_path
end
end
The method update_all receives a single Hash as its sole argument. You need to put all of the updates into the single argument:
user.tasks.update_all(status: status.to_s, id: ids)
# The above line of code is identical to:
# user.tasks.update_all( {status: status.to_s, id: ids} ) # < Notice the curly braces
More information on this method is shown in the Rails Relation Docs

Rails Sort Results based on an association to a child model's method

I've got an Event model. and this Event model has_one Address model. A User model also has_one Address model. I want to get a list of all events in the future, but sorted based on distance between the events address and the users address. This 'distance' is a method on my address model. how can i write the rails query with active record? I've tried many variations of the following but nothing seems to work.
Event.where('end_time > ?', Time.now).includes(Address).order(address.distance_from(User.find(3).primary_address))
i always get this error: NameError: undefined local variable or method `address' for main:Object
definitions of Event and User:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_many :addresses
belongs_to :primary_address, :class_name => "Address"
class Event < ActiveRecord::Base
extend TimeFromPieces
attr_accessor :start_date_string
belongs_to :address
delegate :timezone, to: :address
here is my address model:
class Address < ActiveRecord::Base
belongs_to :user
has_many :events
validates_presence_of :name, :address1, :city, :state, :zip
geocoded_by :address_as_string do |obj, results|
Rails.logger.debug { "results: #{results}" }
obj.lat = results.first.coordinates[0]
obj.lng = results.first.coordinates[1]
Rails.logger.debug { "Neighborhoods: #{results.first.address_components_of_type(:neighborhood)}" }
neighborhoods = results.first.address_components_of_type(:neighborhood)
if neighborhoods.count > 1
obj.neighborhood = neighborhoods.first["long_name"]
end
end
after_validation :geocode, :if => :address1_changed?
after_validation :time_zone_for_address, :if => :address1_changed?
before_save :set_primary_if_only_address_for_user
before_save :normalize_fields
scope :near, ->(lat, lng, distance_in_meters = 2000) {
where(%{
ST_Dwithin(
ST_GeographyFromText(
'SRID=4326;POINT(' || addresses.lng || ' ' || addresses.lat || ')'
),
ST_GeographyFromText('SRID=4326;POINT(%f %f)'),
%d
)
} % [lng, lat, distance_in_meters])
}
def self.distance(address1, address2)
query = %{ SELECT
ST_Distance(
ST_GeographyFromText('SRID=4326;POINT(%f %f)'),
ST_GeographyFromText('SRID=4326;POINT(%f %f)')
)
} % [address1.lng, address1.lat, address2.lng, address2.lat]
meters = ActiveRecord::Base.connection.execute(query).values.first.first.to_i
meters/1609.34
end
def distance_from(address)
Address.distance(self, address)
end
end
Instead of using a ruby geocoder, you could use a gem like geokit-rails, which provides geocoding abilities to active record, it gives you nice methods that allow you to query by distance directly to the database, so you don't need to fetch all objects to calculate their distance
You could say that address is a mappable object, and point the lat and lng fields ( if they aren't using the default names used by the gem )
class Address
acts_as_mappable
end
Then you could say that an event is also mappable through the address object attached to it
class Event
acts_as_mappable through: :address
end
The query would look something like this
Event.where('end_time < ?', Time.now).by_distance(origin: address)

Issue migrating Rails 3.2 :includes call to Rails 4

I'm upgrading a Rails application from v3.2.13 to v4.1.5. Consider the following models.
class Camera::Clip < ActiveRecord::Base
belongs_to :camera
def self.cleanup(opts)
# blah blah method stuff
system_clips = Camera::Clip.includes(:camera).where(['cameras.control_system_id = ?', opts[:control_system_id]])
# In Rails 4, raises Mysql2::Error: Unknown column 'cameras.control_system_id'
end
end
class Camera < ActiveRecord::Base
belongs_to :control_system
has_many :clips, dependent: :destroy
# rest of class
end
I can get the method to function as it had originally by changing the :includes call to :joins, but why is that necessary? I thought :includes used a LEFT OUTER JOIN call on the database to combine the resources?
This may have been lost on me in Release Notes documentation, but a second step is needed to tell the where clause what I mean by 'cameras'.
This documentation pointed me to the :references method.
Swapping out the line for
Camera::Clip.includes(:camera).where('cameras.control_system_id = 13').references(:camera)
shows the exact same output in the Rails console as my environment running in Rails 3.2.13, with no error! Great success.

Using CanCan with Polymorphic Associations

I am struggling to use CanCan with my polymorphic associations. It is breaking down when I try and load the polymorphic objects in my controller.
class Trip < ActiveRecord::Base
has_many :images, as: :viewable
end
class Image < ActiveRecord::Base
belongs_to :viewable, polymorphic: true
end
Now I have a controller that deals specifically with images assigned to a trip.
class TripImagesController < ApplicationController
load_and_authorize_resource :trip
load_and_authorize_resource :image, through: :trip
def index
end
end
However, when I hit this index action I get the following error:
Couldn't find Image with id= [WHERE "images"."viewable_id" = $1 AND "images"."viewable_type" = $2]
I can see the query being executed in the Rails logs:
SELECT "images".* FROM "images" WHERE "images"."viewable_id" = $1 AND "images"."viewable_type" = $2 AND "images"."id" = $3 LIMIT 1 [["viewable_id", 1], ["viewable_type", "Trip"], ["id", ""]]
So the select statement looks good, except that it's trying to find only a single image (even though I'm in an index action). It's specifying an image id (which obviously does not exist in the route params) and it's also limiting the result to a single row.
This is using Rails 4.0.2 and CanCan 1.6.10. What am I missing here?