I'm trying to delete several rows in the table actionable_items via the following migration. I have debugged and can confirm that the variables that store the table row are not nil. The migration runs successfully, but it doesn't delete the row from the table. Also, does anyone know why I can debug a migration when I run rake db:migrate:redo but not when I run rake db:migrate ?
class RemoveActionableItems < ActiveRecord::Migration
class ActionableItem < ActiveRecord::Base
attr_accessible :actionable_item, :name, :sequence, :type
end
class MenuItemTEMP < ActionableItem
self.table_name = "actionable_items"
end
class InsightReportMenuItemTEMP < ActionableItem
self.table_name = "actionable_items"
end
def up
validation_settings = MenuItem.find_by_name("Validation Settings")
identifier_lookup = MenuItem.find_by_name("Identifier Lookup")
compliance = InsightReportMenuItem.find_by_name("Compliance")
debugger
validation_settings.destroy! #unless validation_settings.nil?
identifier_lookup.destroy! #unless identifier_lookup.nil?
compliance.destroy! #unless compliance.nil?
end
def down
MenuItem.create :name => "Validation Settings", :type => "MenuItem"
MenuItem.create :name => "Identifier Lookup", :type => "MenuItem"
InsightReportMenuItem.create :name => "Compliance", :type => "InsightReportMenuItem"
end
end
I also tried deleting from the rails console, but once again, pgAdmin is showing the row not deleted.
pmpaware-webapp(development)> compliance = InsightReportMenuItem.find_by_name("Compliance")
InsightReportMenuItem Load (3.8ms) SELECT "actionable_items".* FROM "actionable_items" WHERE "actionable_items"."type" IN ('InsightReportMenuItem') AND "actionable_items"."name" = 'Compliance' LIMIT 1
=> #<InsightReportMenuItem id: 264, name: "Compliance", actionable_item_id: nil, created_at: "2015-07-23 18:57:25", updated_at: "2015-07-23 18:57:25", actionable_items_count: 0, sequence: nil, type: "InsightReportMenuItem">
pmpaware-webapp(development)> compliance.errors
=> #<ActiveModel::Errors:0x007fc0735ac540 #base=#<InsightReportMenuItem id: 264, name: "Compliance", actionable_item_id: nil, created_at: "2015-07-23 18:57:25", updated_at: "2015-07-23 18:57:25", actionable_items_count: 0, sequence: nil, type: "InsightReportMenuItem">, #messages={}>
pmpaware-webapp(development)> compliance.delete
SQL (111829.8ms) DELETE FROM "actionable_items" WHERE "actionable_items"."type" IN ('InsightReportMenuItem') AND "actionable_items"."id" = 264
=> #<InsightReportMenuItem id: 264, name: "Compliance", actionable_item_id: nil, created_at: "2015-07-23 18:57:25", updated_at: "2015-07-23 18:57:25", actionable_items_count: 0, sequence: nil, type: "InsightReportMenuItem">
FOUND SOLUTION
class RemoveActionableItems < ActiveRecord::Migration
class ActionableItem < ActiveRecord::Base
attr_accessible :actionable_item, :name, :sequence, :type
end
class MenuItemTEMP < ActionableItem
self.table_name = "actionable_items"
end
class InsightReportMenuItemTEMP < ActionableItem
self.table_name = "actionable_items"
end
def up
MenuItem.delete_all(name: "Validation Settings") unless MenuItem.find_by_name("Validation Settings").nil?
MenuItem.delete_all(name: "Identifier Lookup") unless MenuItem.find_by_name("Identifier Lookup").nil?
InsightReportMenuItem.delete_all(name: "Compliance") unless InsightReportMenuItem.find_by_name("Compliance").nil?
end
def down
MenuItem.create :name => "Validation Settings", :type => "MenuItem"
MenuItem.create :name => "Identifier Lookup", :type => "MenuItem"
InsightReportMenuItem.create :name => "Compliance", :type => "InsightReportMenuItem"
end
end
Related
I have models Favorite_Photo, User, and Photo
In Heroku Console:
u = User.find(1)
u.favorites.last
=> #<Photo id: 37, user_id: 1, picture: "th.jpeg", title: "Cookies & Cream Pocky ", description: nil, photo_type: nil, location_type: nil, remote_picture_url: nil, created_at: "2016-07-07 03:04:03", updated_at: "2016-07-07 03:04:03">
And If I query:
u = User.find(1)
u.favorite_photos.last
=> #<FavoritePhoto id: 87, photo_id: 12, user_id: 1, created_at: "2016-07-07 19:37:28", updated_at: "2016-07-07 19:37:28">
class User
has_many :favorite_photos
has_many :favorites, through: :favorite_photos, source: :photo
class Photo
has_many :favorite_photos
has_many :favorited_by, through: :favorite_photos, source: :user
class FavoritePhoto
belongs_to :user
belongs_to :photo
validates :user_id, uniqueness: {
scope: [:photo_id],
message: 'can only favorite an item once'
}
UsersController
def show
#user = User.find(params[:id])
#favorites = #user.favorites
end
This returns a list of favorites ordered by photo_id. I want to create a scope that will order the favorites based on FavoritePhoto id:
has_many :favorites, -> { order("favorite_photos.id ASC") }, through: :favorite_photos, source: :photo
reference: scopes for has_many
I'm having an issue with my scheduled text messages. I run a rake task that checks to see if a text message should be put into a Sidekiq queue. The record is processed (the text is sent) but a new empty record is generated and the sentstatus is not updated to "true".
send_scheduled_text.rake
require_relative '../../app/workers/send_text_worker'
namespace :send_scheduled_text do
task:texts => :environment do
TextMessage.all.each do |text_message|
if text_message.sentstatus == false
if (Date.today == text_message.scheduled_date) && (Time.now.hour >= text_message.scheduled_time.hour)
# Sidekiq code:
SendTextWorker.perform_async(text_message.id)
end
end
end
end
end
send_text_worker.rb
class SendTextWorker
include Sidekiq::Worker
def perform(text_message_id)
text = TextMessage.find(text_message_id)
text.send_text_message(text.content, text.phone)
end
end
text_message.rb
require 'twilio-ruby'
require 'date'
class TextMessage < ActiveRecord::Base
belongs_to :client, dependent: :destroy
belongs_to :step, dependent: :destroy
has_many :coach_emails
before_save :grab_phone
def grab_phone
self.phone = phone
end
def send_text_message(message, phone)
twilio_sid = ENV["TWILIO_ACCT_SID"]
twilio_token = ENV["TWILIO_AUTH_TOKEN"]
twilio_phone_number = ENV["TWILIO_PHONE_NUMBER"]
begin
#twilio_client = Twilio::REST::Client.new(twilio_sid, twilio_token)
#twilio_client.account.sms.messages.create(
:from => "+1#{twilio_phone_number}",
:to => phone,
:body => message)
rescue Twilio::REST::RequestError => e
puts e.message
end
if e != "400" || e != "500"
self.sentstatus = true
end
self.save!
send
send
Rails console: before rake task is called
(sentstatus is false)
irb(main):001:0> TextMessage.all
TextMessage Load (0.5ms) SELECT "text_messages".* FROM "text_messages"
=> #<ActiveRecord::Relation [#<TextMessage id: 164, client_id: nil, content: "Testing Sidekiq processing", incoming_message: false, created_at: "2015-02-02 04:43:29", updated_at: "2015-02-02 04:43:29", scheduled_date: "2015-02-01", sentstatus: false, step_id: 4, phone: "+14127364161", scheduled_time: "2000-01-01 14:00:00">]>
Rails console: After rake task is called
(sentstatus is false, should be true. I also have this new bizarre empty record)
irb(main):001:0> TextMessage.all
TextMessage Load (0.5ms) SELECT "text_messages".* FROM "text_messages"
=> #<ActiveRecord::Relation [#<TextMessage id: 164, client_id: nil, content: "Testing Sidekiq processing", incoming_message: false, created_at: "2015-02-02 04:43:29", updated_at: "2015-02-02 04:43:29", scheduled_date: "2015-02-01", sentstatus: false, step_id: 4, phone: "+14127364161", scheduled_time: "2000-01-01 14:00:00">,
#<TextMessage id: 165, client_id: nil, content: nil, incoming_message: nil, created_at: "2015-02-02 04:45:24", updated_at: "2015-02-02 04:45:24", scheduled_date: nil, sentstatus: true, step_id: nil, phone: nil, scheduled_time: nil>]>
I have a feeling this is a Sidekiq nuance that I'm missing. Thanks for any thoughts!
I ended up moving my model logic to my worker. Presto -- timing works and I'm not generating any extra nil records.
I have...
/app/models/input.rb:
class Input < ActiveRecord::Base
has_many :questions, :dependent => :destroy
after_commit :create_matching_questions
def create_matching_questions
#element_id = Element.all.select{|e| e.meta == true}.first.id
#standard_id = Standard.all.select{|s| s.meta == true}.first.id
#description = ["Does the site stock ", self.name, "?"].join
Product.all.each do |product|
#question = product.questions.find_or_create_by_element_id_and_standard_id_and_description!(#element_id, #standard_id, #description)
self.questions << #question
#question.fields.find_or_create_by_name("The site sells this product and it is in stock")
#question.fields.find_or_create_by_name("The site sells this product but it is not in stock")
#question.fields.find_or_create_by_name("The site does not sell this product")
#question.update_attributes :active => true
end
return true
end
end
/app/models/question.rb:
class Question < ActiveRecord::Base
belongs_to :input
after_commit :create_matching_surveys
def create_matching_surveys
if self.active == true
self.reload.product.reviews.each do |review|
review.competitors.each do |competitor|
(1..self.iterations).each do |iteration|
survey = competitor.surveys.find_or_create_by_question_id_and_iteration!(self.id, iteration)
survey.save
end
end
end
return true
else
self.destroy_matching_surveys
end
end
def destroy_matching_surveys
self.surveys.each do |survey|
survey.destroy if survey.question_id == self.id
end
return true
end
end
Why, then, do I get...
> #finance = Good.create! :name => "Finance"
=> #<Good id: 6, name: "Finance", created_at: "2013-06-13 02:56:20", updated_at: "2013-06-13 02:56:20">
> #super = Input.create! :name => "Superannuation"
=> #<Input id: 11, name: "Superannuation", mispelling: nil, typo: nil, created_at: "2013-06-13 02:56:28", updated_at: "2013-06-13 02:56:28">
> #first = #super.questions.first
=> #<Question id: 48, standard_id: 1, description: "Does the site stock Superannuation?", element_id: 2, condition_id: nil, blueprint_name: nil, blueprint_url: nil, additive: false, instructions: nil, created_at: "2013-06-13 02:56:41", updated_at: "2013-06-13 02:56:41", active: false, postscript: "<p>If you have any comments about this question or ...", iterations: 1, product_id: 1, precondition_id: nil, input_id: 11>
> #last = #super.questions.last
=> #<Question id: 60, standard_id: 1, description: "Does the site stock Superannuation?", element_id: 2, condition_id: nil, blueprint_name: nil, blueprint_url: nil, additive: false, instructions: nil, created_at: "2013-06-13 02:56:43", updated_at: "2013-06-13 02:56:43", active: false, postscript: "<p>If you have any comments about this question or ...", iterations: 1, product_id: 23, precondition_id: nil, input_id: 11>
> #super.destroy
=> #<Input id: 11, name: "Superannuation", mispelling: nil, typo: nil, created_at: "2013-06-13 02:56:28", updated_at: "2013-06-13 02:56:28">
> #super.destroyed?
=> true
> #first.destroyed?
=> false
> #last.destroyed?
=> false
Surely #first and #last should be destroyed automatically?
I had the same problem, solved it by :dependent => :delete_all instead of :dependent => :destroy.
:delete_all doesn't call the destroy method from your controller and delete data directly from your database.
I would like to perform:
XXX.find_or_build_by_language_id(attributes)
I found
XXX.find_or_initialize_by_language_id(attributes)
but that only set language_id and no other attributes. Even if I manually sets the attributes, the record is not saved when I perform XXX.save.
I just read Rails - find or create - is there a find or build?, which seems related to my problem but does not fit my needs.
Edit
Let's use this scenario
# db/migrations/create_models.rb
class CreateModels < ActiveRecord::Migration
def self.up
create_table :companies do |t|
t.string :name
end
create_table :employees do |t|
t.string :name
t.string :city
t.references :company
end
end
end
-
# app/models/employee.rb
class Employee < ActiveRecord::Base
belongs_to :company
end
-
# app/models/company.rb
class Company < ActiveRecord::Base
has_many :employees
end
-
# rails console
:001> c = Company.new
=> #<Company id: nil, name: nil>
:002> c.employees
=> []
:003> e = c.employees.find_or_initialize_by_name(:name => 'foo', :city => 'bar')
=> #<Employee id: nil, name: "foo", city: "bar", company_id: nil>
:004> c.employees
=> []
:005> c.save
=> true
:006> c.employees
=> []
:007> e.save
=> true
:008> c = Company.first
=> #<Company id: 1, name: nil>
:009> c.employees
=> [#<Employee id: 1, name: "foo", city: "bar", company_id: 1>]
:010> e = c.employees.find_or_initialize_by_name(:name => 'foo', :city => 'baz')
=> #<Employee id: 1, name: "foo", city: "bar", company_id: 1>
:011> e.city = 'baz'
=> "baz"
:012> c.employees
=> [#<Employee id: 1, name: "foo", city: "bar", company_id: 1>]
:013 > c.save
=> true
:014> c.employees
=> [#<Employee id: 1, name: "foo", city: "bar", company_id: 1>]
Problems
:004 => The Employee from :003 is not added to c.employees
:006 => The Employee from :003 is saved with c
:010 => The city attribute of employee is not set
:014 => THe city attribute of employee is not updated when saving company
How about this?
employee_attrs = {:name => 'foo', :city => 'bar'}
e = c.employees.where(employee_attrs).first || c.employees.build(employee_attrs)
For the record, here is the implementation I came with. It can probably be simpler, but it suits my needs:
module ActiveRecord
module Associations
class AssociationCollection < AssociationProxy
alias_method :old_method_missing, :method_missing
def method_missing(method_id, *arguments, &block)
if /^find_or_build_by_([_a-zA-Z]\w*)$/ =~ method_id.to_s
names = $1.split('_and_')
find_or_build_by(names, *arguments)
else
old_method_missing(method_id, *arguments, &block)
end
end
def find_or_build_by(names, *arguments)
values = arguments[0]
throw InvalidArgument unless values.keys.first.kind_of?(String)
record = Array.new(self).find do |r|
names.inject(true) do |memo, name|
memo && (values[name].to_s == r.send(name).to_s)
end
end
if record
sanitized_values = record.send(:sanitize_for_mass_assignment, values)
sanitized_values.each {|k, v| record.send("#{k}=", v)}
else
record = build(values)
end
return record
end
end
end
end
I tried the following code for my Rails 4.2.x app.
#config/initializers/collection_proxy.rb
ActiveRecord::Associations::CollectionProxy.class_eval do
alias_method :old_method_missing, :method_missing
def method_missing(method_id, *arguments, &block)
if /^find_or_build_by([_a-zA-Z]\w*)$/ =~ method_id.to_s
names = $1.split('_and_')
find_or_build_by(names, *arguments)
else
old_method_missing(method_id, *arguments, &block)
end
end
def find_or_build_by(names, *arguments)
where(names).first || build(names)
end
end
You can use it like this.
XXX.find_or_build_by(attributes)
The problem I am having with this is Product is trying to create variants before the product is even created and there are certain callbacks for variants that require the product to exist. So how can I rewrite this so that v.save doesn't execute till the object is created or whatever.
Product.class_eval do
validates_presence_of [:style_no, :market_price, :designer, :short_description, :description]
validates_numericality_of [:size_47_quantity,
:size_46_quantity,
:size_45_quantity,
:size_44_quantity,
:size_43_quantity,
:size_42_quantity,
:size_41_quantity,
:size_40_quantity,
:size_39_quantity]
for i in 39..47
define_method:"size_#{i}_quantity" do
if v = self.variants.find_by_size(i)
v.count_on_hand
else
0
end
end
define_method:"size_#{i}_quantity=" do |amount|
# if only there is some method that can postpone all the following if this product hasn't been created yet!
self.id = Product.last.id + 1 unless self.id
v = self.variants.find_by_size(i) || self.variants.new(:size => i)
v.count_on_hand = amount
v.save
end
end
end
You can try this solution:
Product class
class Product < ActiveRecord::Base
validates_presence_of [:style_no, :market_price, :designer, :short_description, :description]
has_many :variants
# This method would check if variant was created or loaded.
#
# So many sequantial calls to it will return same object
def variant_with_size(size)
self.variants.select{|v| v.size == size}.first || self.variants.where('size = ?', size).first
end
module ClassExtensions
def self.included(base)
(39..47).each do |i|
method = "size_#{i}_quantity".to_sym
included_module = Module.new
included_module.module_eval <<EOF
def #{method}
if v = self.variant_with_size(#{i})
v.count_on_hand
else
0
end
end
def #{method}=(amount)
v = self.variant_with_size(#{i}) || self.variants.build(:size => #{i})
v.count_on_hand = amount
v
end
EOF
base.send :include, included_module
end
end
end
include ClassExtensions
end
Variant class
class Variant < ActiveRecord::Base
belongs_to :product
validates :count_on_hand, :numericality => true
end
Usage
Usage example with correct variant amount:
ruby-1.9.2-p180 :001 > p = Product.new
=> #<Product id: nil, style_no: nil, market_price: nil, designer: nil, short_description: nil, description: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p180 :002 > p.size_39_quantity
=> 0
ruby-1.9.2-p180 :003 > p.size_39_quantity = 2
=> 2
ruby-1.9.2-p180 :004 > p.variants
=> [#<Variant id: nil, product_id: nil, size: 39, count_on_hand: 2, created_at: nil, updated_at: nil>]
ruby-1.9.2-p180 :005 > p.save
=> true
ruby-1.9.2-p180 :006 > p.variants
=> [#<Variant id: 3, product_id: 3, size: 39, count_on_hand: 2, created_at: "2011-04-06 06:34:46", updated_at: "2011-04-06 06:34:46">]
Usage with incorrect variant amount:
ruby-1.9.2-p180 :007 > p1 = Product.new
=> #<Product id: nil, style_no: nil, market_price: nil, designer: nil, short_description: nil, description: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p180 :008 > p1.size_39_quantity = 'A'
=> "A"
ruby-1.9.2-p180 :009 > p1.save
=> false
ruby-1.9.2-p180 :010 > p1.errors
=> {:variants=>["is invalid"]}
ruby-1.9.2-p180 :011 > p1.variants[0].errors
=> {:count_on_hand=>["is not a number"]}
At a glance, I'd consider using an after_save callback on Product to create product variants.
Something like:
class Product < ActiveRecord::Base
has_many :variants
after_save :create_variants! if :not_a_variant?
OPTIONS = [:size_1_qty, :size_2_qty] # TODO: move to a OptionType model associated with Product
def not_a_variant?
size.nil? # or however you might distinguish a Product from a Variant
end
private
def create_variants!
# OPTIONS could instead be related option_types. perhaps a 'size' option type with values of 40, 41, 42, etc.
OPTIONS.each do |size|
variants.build(...)
end
save!
end
end
I was just reviewing the Spree shopping cart project by Rails Dog and they handle product variants in a similar fashion. You might check it out.