Rails 4 update_all syntax - Argument error - ruby-on-rails-4

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

Related

Rails 4 - Paperclip - Pass image name as method parameter and remove it inside it

I have several paperclip attachments in a Model. I want to pass the name of it as a parameter for a method and then set it to nil.
class Campaign < ActiveRecord::Base
has_attached_file :header_image
has_attached_file :footer_image
end
class CampaignsController < ApplicationController
def remove_image(image_name)
#campaign.image_name = nil # <- I want to do something like this
end
end
How to do something like this? Is there a better way?

Rails 4 - association not working correctly

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

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)

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?

Rails 4.0.4: How do I display whether or not the creation of a record was successful?

So After submitting a form for my Testimony model I get the following activity in my rails console:
Processing by TestimoniesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Y7n/+rlDoH3ys68HMOh6T6WFpAelRT18WUPstCz41vE=", "testimony"=>{"first_name"=>"bob ", "last_name"=>"balaban", "email"=>"", "contact_number"=>"", "country"=>"", "question1"=>"", "question2"=>"", "question3"=>"", "question4"=>"", "question5"=>"", "signature"=>"", "waiver"=>"0"}, "commit"=>"Save Testimony"}
(0.1ms) begin transaction
Testimony Exists (0.2ms) SELECT 1 AS one FROM "testimonies" WHERE ("testimonies"."last_name" = 'balaban' AND "testimonies"."first_name" = 'bob ' AND "testimonies"."email" = '') LIMIT 1
(0.1ms) rollback
Here is TestimoniesController
class TestimoniesController < ApplicationController
def new
end
def index
end
def show
#testimony = Testimony.find(params[:id])
end
def create
#testimony = Testimony.new(post_params)
#testimony.save
redirect_to #testimony
end
private
def post_params
params.require(:testimony).permit(:first_name, :email, :last_name, :contact_number, :country, :question1, :question2, :question3, :question4, :question5, :signature, :waiver)
end
end
Here is my Model file for Testimony.rb
class Testimony < ActiveRecord::Base
validates_presence_of :first_name, :last_name,:email, :contact_number, :country, :question1, :question2, :question3, :question4, :question5, :signature, :waiver
validates_uniqueness_of :last_name, :scope => [:first_name, :email]
end
I would like to display a message such as:
Record already exists
Or
All the fields need to be filled out
I realize this is pretty pathetic but I thought it was part of Rails magic to assert the uniqueness and presence of all of the fields of a Model at the form level, before you perform a #modelname.save. I clearly don't really understand. Thanks for your help.
if #modelname.save! failed it'll raise an exception... the exception name varies depending on the orm you might be using. but most likely it'll be ActiveModel::Validations
you can use the following pattern i myself use
#testimony.save!
redirect_to #testimony
rescue_from ActiveModel::Validations do |ex|
# here you can flash the message do what ever you want when saving fails
render json: {message: 'Record already exists', status: :unprocessable_entity
end
other solution is to use the following
if #testimony.save
redirect_to #testimony
else
render json: {message: 'Record already exists', status: :unprocessable_entity
end
[EXTRA INFO YOU CAN IGNORE :D]
I use the first solution cause i can handle exceptions in the application_controller.
for example you can add those to the application_controller to handle those failing cases
rescue_from CanCan::AccessDenied, with: :render_access_denied
rescue_from Mongoid::Errors::DocumentNotFound, with: :render_not_found
rescue_from ActionController::RoutingError, with: :render_not_found
rescue_from(ActionController::ParameterMissing) do |parameter_missing_exception|
error = {}
error[parameter_missing_exception.param] = ['parameter is required']
render_bad_request error
end
where render_XXX are methods defined in the application controller.