I'm trying to modify my existing callback on a App model to be executed via delayed job. I am getting a error undefined local variable or method app_name for main:Object, when deleted an app.
app/models/app.rb
# == Schema Information
#
# Table name: apps
#
# id :integer not null, primary key
# name :string(255)
# created_at :datetime
# updated_at :datetime
# app_type :string(255)
# package_name :string(255)
# icon :string(255)
#
# app/models/app.rb
class App < ActiveRecord::Base
has_many :versions, dependent: :destroy
app/models/version.rb
class Version < ActiveRecord::Base
DEFAULT_ICON_URL = 'placeholder_med#2x.png'
belongs_to :app
delegate :name, :id, :users, :app_type, to: :app, prefix: true
after_create :notify_subscribers
before_destroy :remove_stored_files
scope :since, ->(time) { where('created_at > ?', time) }
def updated_or_created_at
updated_at || created_at
end
def display_icon
if icon_url.blank? || icon_url.match(/default.png/)
DEFAULT_ICON_URL
else
icon_url
end
end
def main?
version_type == 'main'
end
def release_notes?
!release_notes_url.blank?
end
private
def notify_subscribers
AppMailer.notify_new_build(id)
end
def remove_stored_files
Delayed::Job.enqueue(DeleteAppFilesJob.new(app_name, version_number, build_number), priority: 1, run_at: 5.minute.from_now)
end
end
app/jobs/delete_app_files_job.rb
class DeleteAppFilesJob < Struct.new(app_name, version_number, build_number)
def perform
remove_stored_files(app_name, version_number, build_number)
end
protected
def remove_stored_files(app_name, version_number, build_number)
S3_BUCKET.objects.select { |obj| obj.key.match(%r{(ios|android)/#{app_name}/#{version_number}/#{build_number}}) }.each do |obj|
puts "Deleting #{obj.key}"
obj.delete
end
end
end
To create an anonymous Struct (which you generally want when subclassing like that), you pass Symbol arguments to Struct.new:
class DeleteAppFilesJob < Struct.new(:app_name, :version_number, :build_number)
You're trying to pass variables that don't exist, hence the "undefined local variable or method" error.
Related
Say I have a Rails model and I want to get the state of a list of an association before and after an update to see the difference between them. How would I go about doing this? I had tried just doing a before_update and after_update callback to maintain an array of the association and taking a rejection of what was in the array after the update compared to before the update but it seems that my array is never matching the state before the update.
Any ideas?
# == Schema Information
#
# Table name: lectures
#
# id :integer not null, primary key
# organization_id :integer
# training_type_id :integer
# name :string(255)
# description :text
# start_date :date
# end_date :date
# training_method :string(255)
# trainer_name :string(255)
# trainer_phone :string(255)
# trainer_email :string(255)
# location :string(255)
# total_seats :integer
# available_seats :integer
# hours :integer
# created_at :datetime
# updated_at :datetime
#
class Lecture < ActiveRecord::Base
# # # # # # # # # # # # # # #
# Relations
# # # # # # # # # # # # # # #
belongs_to :organization
belongs_to :training_type
has_many :training_histories
has_many :users, through: :training_histories
# belongs_to :training_type
# has_many :training_histories, inverse_of: :lecture, :autosave => true
accepts_nested_attributes_for :training_histories
# # # # # # # # # # # # # # #
# Validations
# # # # # # # # # # # # # # #
validates :name,
:start_date,
:end_date,
:location,
:organization,
:trainer_email,
:trainer_name,
:hours,
:presence => :true
validate :start_must_be_before_end_date
validates :trainer_email, email: true
after_update :email_users_about_update, email_supervisors_about_signup
def start_must_be_before_end_date
errors.add(:start_date, "must be before end date") unless self.start_date <= self.end_date
end
def email_users_about_update
self.training_histories.each do |history|
UserMailer.updated_training_email(history.user, self).deliver unless history.completed
end
end
def email_supervisors_about_signup
binding.pry
if self.users.changed?
users_dif = self.users - self.users_was
unless users_dif.nil?
users_dif.each do |user|
UserMailer.create_subordinate_training_sign_up(user.leader, user, self).deliver
end
end
end
end
end
You can use an after_add callback:
# add the callback to our association
has_many :users, through: :training_histories, after_add: :invite_to_training
# this method gets called when a user is added
private
def invite_to_training(user)
UserMailer.create_subordinate_training_sign_up(user.leader, user, self).deliver
end
For more information on association callbacks see: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Association+callbacks
An Email has many Variants (for ab testing purposes) and always has one set as the master
I want to ensure an email always has a master variant built on initialization.
I also want to delegate attr accessors 'subject' and 'body' to the master variant.
I originally tried using
delegate :subject, :body, to: :master
but rails complained master was nil.
So I tried hand rolling my own subject= setter method and via pry I found that whilst my master is being set in the after_initialize callback, the subsequent call to subject= complains master is nil. I dont understand why.
class Email < ActiveRecord::Base
has_one :master,
-> { where is_master: true },
class_name: 'Tinycourse::Variant',
dependent: :destroy,
inverse_of: :email
def subject=(str)
master.subject = str # Rails says master is nil here
end
#
# Callbacks
#-----------------------------------------
after_initialize :ensure_master
def ensure_master
return unless new_record?
self.master ||= build_master
end
end
Email.new(:subject => 'yah') # undefined method `subject=' for nil:NilClass
when your email instance is initialized your master is nil, you need to trigger build_master before you set anything on it
how about:
# app/models/email.rb
class Email < ActiveRecord::Base
def subject=(str)
master.subject = str # Rails says master is nil here
end
def master
super || build_master
end
end
Don't know much when and why you need after_initialize callback in your project, but if you sure you need this functionality, I would consider to use custom service class to achieve this
# app/models/email.rb
class Email < ActiveRecord::Base
has_one :master,
-> { where is_master: true },
class_name: 'Tinycourse::Variant',
dependent: :destroy,
inverse_of: :email
def subject=(str)
master.subject = str # this way Rails won't says master is nil here
end
end
# app/lib/email_builder.rb
class EmailBuilder
attr_reader :args
def self.build(args={})
new(args).build
end
def initialize(args)
#args = args
end
def build
email = Email.new
email.build_master
email.attributes = args
email
end
end
email = EmailBuilder.build subject: 'yah'
email.class # => Email
...and another variation of this for persisted record
Update
or even better
# app/models/email.rb
class Email < ActiveRecord::Base
has_one :master,
-> { where is_master: true },
class_name: 'Tinycourse::Variant',
dependent: :destroy,
inverse_of: :email
end
# app/lib/email_builder.rb
class EmailBuilder
attr_reader :args, :subject
def self.build(args={})
new(args).build
end
def initialize(args)
#subject = args.fetch(:subject)
#args = args
end
def build
email = Email.new args
email.build_master
email.master.subject = subject
email
end
end
email = EmailBuilder.build subject: 'yah'
email.class # => Email
In my Organisations module, I try to assign a value to a field before creating the Organisation record.
I use a before_create filter in the model, which usually works fine.
But when I try to assign a value coming from an attribute of the current_user method defined in the session, I get an Undefined method 'current_user' error message.
As doing this works fine in a controller, I wonder why it does not work in the model?
Here is my code for the model:
# == Schema Information
#
# Table name: organisations
#
# id :integer not null, primary key
# playground_id :integer
# code :string(255)
# name :string(255)
# description :text
# parent_organisation_id :integer
# organisation_level :integer
# hierarchy :string(255)
# created_by :string(255)
# updated_by :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
class Organisation < ActiveRecord::Base
### before filter
before_create :set_code
before_create :set_hierarchy
validates :code, presence: true, uniqueness: true
validates :name, presence: true, uniqueness: true
validates :organisation_level, presence: true
validates :created_by , presence: true
validates :updated_by, presence: true
# validates :owner_id, presence: true
# validates :status_id, presence: true
validates :playground_id, presence: true
# belongs_to :owner, :class_name => "User", :foreign_key => "owner_id" # helps retrieving the owner name
# belongs_to :status, :class_name => "Parameter", :foreign_key => "status_id" # helps retrieving the status name
belongs_to :organisation
has_many :organisations
### private functions definitions
private
### before filters
def set_code
if Organisation.count > 0
self.code = self.organisation.code + '-' + code
end
end
def set_hierarchy
if Organisation.count == 0
self.hierarchy = current_user.current_playground_id.to_s + '.001'
else
last_one = Organisation.maximum("hierarchy")
self.hierarchy = last_one.next
end
end
end
Here is my code for the SessionsHelper (inspired from Rails Tutorial):
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.encrypt(remember_token))
self.current_user = user
end
def current_user=(user)
#current_user = user
end
def current_user
remember_token = User.encrypt(cookies[:remember_token])
#current_user ||= User.find_by(remember_token: remember_token)
end
def signed_in?
!current_user.nil?
end
def sign_out
self.current_user = nil
cookies.delete(:remember_token)
end
end
Here is an example from a controller where the assignment works:
#business_flow.playground_id = current_user.current_playground_id
I'd be glad to understand why it does not work in the model.
The SessionsHelper module gets included into your controller class, so you can use its methods from there. It does not get included in the model class.
In fact it probably wouldn't work even if you included it by hand, because the model would need access to the cookies method for the current_user method to work. cookies comes from an ActionController method.
To achieve what you want, try making the change in your Organisation object from the controller that's creating that object, passing in the user information that the controller has access to.
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
undefined method `to_f' for #<ActiveRecord::Relation:0x472d0a0>
I'm trying to make a call tracking application to learn twilio and rails.
Right now, I would like to make a graph that shows a user how many phone calls a particular phone number gets per day.
The schema is user has_many phones has_many calls.
I try to make the graph by creating an instance method that counts the number of phones on a particular day, but when I try executing the code, I get the error :
SQLite3::SQLException: no such column: calls.placed_at: SELECT COUNT(*) FROM "calls" WHERE "calls"."phone_id" = 44 AND ("calls"."placed_at" BETWEEN '2012-09-15 00:00:00.000000' AND '2012-09-15 23:59:59.999999')
I don't quite understand the code I'm using for the instance method, and it's probably calling the wrong column. Your help on this would be greatly appreciated.
Here's the important part of my call model:
def total_on(date)
calls.where(placed_at: date.beginning_of_day..date.end_of_day).count
end
Here's how I'm counting the phone calls in my show view
<%= (1.month.ago.to_date..Date.today).map { |date| #phone.total_on(date).to_f}.inspect %>
Here's how I define the #phone variable
#phone = Phone.find_by_id(params[:id])
Here's my complete phone model (for schema reference)
# == Schema Information
#
# Table name: phones
#
# id :integer not null, primary key
# name :string(255)
# twilio_number :integer
# original_number :integer
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Phone < ActiveRecord::Base
attr_accessible :original_number, :user_id, :name, :twilio_number
belongs_to :user
has_many :calls, dependent: :destroy
validates :name, presence: true
validates :twilio_number, presence: true
validates :original_number, presence: true
validates :user_id, presence: true
default_scope order: 'phones.created_at DESC'
validate :check_phone_limit, :on => :create
def check_phone_limit
if User.find(self.user_id).at_max_phone_limit?
self.errors[:base] << "Cannot add any more phones"
end
end
def original_number=(value)
num = value.to_s.gsub(/[^0-9+]/, "")
write_attribute(:original_number, num.to_i)
end
def total_on(date)
calls.where(placed_at: date.beginning_of_day..date.end_of_day).count
end
end
Here's my complete call model
# == Schema Information
#
# Table name: calls
#
# id :integer not null, primary key
# AccountSid :string(255)
# From :string(255)
# To :string(255)
# CallStatus :string(255)
# ApiVersion :string(255)
# Direction :string(255)
# FromCity :string(255)
# FromState :string(255)
# FromZip :string(255)
# FromCountry :string(255)
# ToCity :string(255)
# ToState :string(255)
# ToZip :string(255)
# ToCountry :string(255)
# CallSid :string(255)
# DialCallSid :string(255)
# DialCallDuration :string(255)
# DialCallStatus :string(255)
# RecordingUrl :string(255)
# phone_id :integer
# DialCallMinutes :integer
# created_at :datetime
# updated_at :datetime
#
class Call < ActiveRecord::Base
attr_accessible :AccountSid, :From, :To, :CallStatus, :ApiVersion, :Direction, :FromCity, :FromState, :FromZip, :FromCountry, :ToCity, :ToState, :ToZip, :ToCountry, :CallSid, :DialCallSid, :DialCallDuration, :DialCallStatus, :RecordingUrl, :DialCallMinutes
belongs_to :phone
def self.create_from_incoming_call(params)
user_phone = Phone.find_by_twilio_number(params['To']) #Finds the phone number in the database based on what phone Twilio is calling
twilio_request_params = {
:CallSid => params['CallSid'],
:AccountSid => params['AccountSid'],
:From => params['From'],
:To => params['To'],
:CallStatus => params['CallStatus'],
:ApiVersion => params['ApiVersion'],
:Direction => params['Direction'],
:FromCity => params['FromCity'],
:FromState => params['FromState'],
:FromZip => params['FromZip'],
:FromCountry => params['FromCountry'],
:ToCity => params['ToCity'],
:ToState => params['ToState'],
:ToZip => params['ToZip'],
:ToCountry => params['ToCountry']
:phone_id => user_phone.phone_id
}
call = Call.new(twilio_request_params)
call.save
return call
end
def Call.update_dial_call(params)
twilio_request_params = {
:DialCallSid => params['DialCallSid'],
:DialCallDuration => params['DialCallDuration'],
:DialCallStatus => params['DialCallStatus'],
:RecordingUrl => params['RecordingUrl'],
:DialCallMinutes => (params['DialCallDuration'].to_f/60.to_f).ceil
}
call = Call.where( :CallSid => params['CallSid'] ).first
call.update_attributes twilio_request_params
call.save
end
end
I've been stuck on this for a while; any help would be greatly appreciated!
Your call model uses the standard rails created_at, yet your query was using placed_at, which doesn't exist.
This is the error I get:
ContactPostalcardsController#skip (NoMethodError) "undefined method `status=' for #<ContactPostalcard:0x2b21433d64b0>"
This is the code calling it and trying to assign a value to the status attribute for ContactPostalcard (the Model):
def skip
#contact_postalcard = ContactPostalcard.new(params[:contact_postalcard])
#contact_postalcard.contact_id = params[:contact_id]
#contact_postalcard.postalcard_id = params[:postalcard_id]
#contact_postalcard.status = "skipped"
#contact_postalcard.date_sent = Date.today
#contact_postalcard.date_created = Date.today
if #contact_postalcard.save
render :text => 'This email was skipped!'
end
end
This is the Model referred. Note the "annotate" output shows status as an attribute:
class ContactPostalcard < ActiveRecord::Base
attr_accessible :title, :contact_id, :postal_id, :postalcard_id, :message, :campaign_id, :date_sent, :status
belongs_to :contact
belongs_to :postalcard
alias_attribute :body, :message
alias_attribute :subject, :title
named_scope :nosugar, :conditions => { :sugarcrm => false }
def company_name
contact = Contact.find_by_id(self.contact_id)
return contact.company_name
end
def asset
Postalcard.find_by_id(self.postalcard_id)
end
def asset_class
Postalcard.find_by_id(self.postalcard_id).class.name
end
end
# == Schema Information
#
# Table name: contact_postalcards
#
# id :integer not null, primary key
# title :string(255)
# contact_id :integer
# postalcard_id :integer
# message :text
# campaign_id :integer
# date_sent :datetime
# created_at :datetime
# updated_at :datetime
# postal_id :integer
# sugarcrm :boolean default(FALSE)
# status :string(255)
#
I am unclear as to why I keep getting an 'undefined method' -- I have added the status attribute (it had been missing before but used a migration and then raked), so need some help...thank you.
Have you restarted your Rails application since you ran your migration? If you're running in production mode, Rails caches your classes until you restart it, and since status wasn't an attribute before the migration, Rails wouldn't have added accessor methods for it, which would explain why status= is undefined.