Validate presence of shipping address unless it's same as billing address - ruby-on-rails

I have this in my Order class. I want to validate the presence of shipping_address unless it's the same as the billing_address. My specs keep failing, however.
class Order < ActiveRecord::Base
attr_writer :ship_to_billing_address
belongs_to :billing_address, class_name: 'Address'
belongs_to :shipping_address, class_name: 'Address'
accepts_nested_attributes_for :billing_address, :shipping_address
validates :shipping_address, presence: true, unless: -> { self.ship_to_billing_address? }
def ship_to_billing_address
#ship_to_billing_address ||= true
end
def ship_to_billing_address?
self.ship_to_billing_address
end
end
But I keep getting failed specs (expected example not to be valid):
describe "shipping_address_id" do
context "when shipping address is different from billing address" do
before { #order.ship_to_billing_address = false }
it_behaves_like 'a foreign key', :shipping_address_id
end
end
shared_examples 'a foreign key' do |key|
it "can't be nil, blank, or not an int" do
[nil, "", " ", "a", 1.1].each do |value|
#order.send("#{key}=", value)
#order.should_not be_valid
end
end
end
Form code:
= f.check_box :ship_to_billing_address
| Use my shipping address as my billing address.

Your ship_to_billing_address method implementation is wrong. It sets #ship_to_billing_address to true even if was set to false before. This is more correct implementation:
def ship_to_billing_address
#ship_to_billing_address = true if #ship_to_billing_address.nil?
#ship_to_billing_address
end
Examples:
irb(main):001:0> class Order
irb(main):002:1> attr_writer :stba
irb(main):003:1> def stba
irb(main):004:2> #stba ||= true
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0>
irb(main):008:0* o = Order.new
=> #<Order:0x8bbc24c>
irb(main):009:0> o.stba = false
=> false
irb(main):010:0> o.stba
irb(main):011:0> class Order2
irb(main):012:1> attr_writer :stba
irb(main):013:1> def stba
irb(main):014:2> if #stba.nil?
irb(main):015:3> #stba = true
irb(main):016:3> else
irb(main):017:3* #stba
irb(main):018:3> end
irb(main):019:2> end
irb(main):020:1> end
=> nil
irb(main):021:0> o = Order2.new
=> #<Order2:0x8b737e0>
irb(main):022:0> o.stba = false
=> false
irb(main):023:0> o.stba
=> false

Related

undefined method `__metadata' for #<Participant:0x00000001076da378> with rails 6 / mongoid

I have the follow code that is working in rails 5. Updagrate to 6 I get the error undefined method `__metadata'.
Here's the problematic code
*
def nature
self.__metadata.key.to_s.singularize.to_sym #
end
*
Have try to use method but it doesn't return what it does in rails 5 / mongoid. Mongoid version is '~> 7.0'
Complete class code
# Participant model class definition
class Participant
include Mongoid::Document
include Mongoid::Timestamps
include DryValidation
field :address
field :identifier
field :name
field :birthdate, type: Date
field :sex
field :profession
field :phone
field :email
field :ownership_percentage
field :contribution_amount
field :category
field :group
field :registered_on, type: Date
field :retired, type: Boolean
field :retired_on, type: Date
field :committee
# Callbacks
before_save :generate_identifier
# Relations
embedded_in :book, inverse_of: :shareholders
embedded_in :book, inverse_of: :directors
embedded_in :book, inverse_of: :employees
embedded_in :book, inverse_of: :committee_members
embeds_many :participant_files
accepts_nested_attributes_for :participant_files, allow_destroy: true
#Validations
validates :name, presence: true
validates :email, allow_blank: true, format: { with: /\A\S+#\S+\.\S+\z/i }
validates :registered_on, presence: true, non_existent_date: true
validates :birthdate, non_existent_date: true
validates :retired_on, non_existent_date: true
validate :registered_on_date
def self.options_for(field_name)
case field_name.to_sym
when :category then [nil, :founders, :actives, :participants]
when :sex then [nil, :male, :female]
when :group then [nil, :legal, :accounting, :human_resources, :consumer, :employee,
:management_and_administration, :communication_and_marketing,
:ethic_and_gouvernance, :other]
else []
end
end
def self.ordered
# This should be as simple as .order_by(:retired_on.desc, :registered_on.asc)
# but the registered_on parameters is never ordered correctly so I had to do this ugly thing :(
self.all.sort_by{ |a| [ (a.retired_on ? a.retired_on.strftime('%Y%m%d') : 0), (a.registered_on ? a.registered_on.strftime('%Y%m%d') : 0) ].join }
end
def self.ordered_by_name
participants = self.active.sort_by{ |p| p.name.downcase }
participants += self.inactive.sort_by{ |p| p.name.downcase }
participants
end
def self.active
now = Time.now.strftime('%Y%m%d')
self.all.select do |a|
if a.registered_on
if a.retired_on
a.retired_on.strftime('%Y%m%d') >= now && a.registered_on.strftime('%Y%m%d') <= now
else
a.registered_on.strftime('%Y%m%d') <= now
end
end
end
end
def self.inactive
now = Time.now.strftime('%Y%m%d')
self.all.select do|a|
(a.retired_on && a.retired_on.strftime('%Y%m%d') < now) ||
(a.registered_on && a.registered_on.strftime('%Y%m%d') > now)
end
end
def book
self._parent
end
def committee_member?
self.nature == :committee_member
end
def director?
self.nature == :director
end
def employee?
self.nature == :employee
end
def nature
self.__metadata.key.to_s.singularize.to_sym #
end
def active?
!retired?
end
def retired?
self.retired_on && self.retired_on <= Time.zone.today
end
def shareholder?
self.nature == :shareholder
end
def securities
self.book.transactions.any_of({from: self.id}, {to: self.id}).asc(:transacted_on)
end
def save_files
self.participant_files.each do |pf|
pf.save
end
delete_objects_without_file
end
def has_shares?
book.share_categories.each do |sc|
return true if total_shares(sc) > 0
end
false
end
def total_shares(share_category)
total = 0
securities.each do |s|
if s.share_category == share_category
if s.nature == 'issued' or (s.nature == 'transfered' and self.id.to_s == s.to.to_s)
total += s.quantity if s.quantity
elsif s.nature == 'repurchased' or (s.nature == 'transfered' and self.id.to_s == s.from.to_s)
total -= s.quantity if s.quantity
end
end
end
total
end
def share_class_percentage(sc)
book.share_class_quantity(sc) > 0 ? self.total_shares(sc)/book.share_class_quantity(sc).to_f*100 : 0
end
def acceptance_documents
self.book.documents.select{|document| document.participant_id == self.id && document.nature == 'dir_accept'}
end
def resignation_documents
self.book.documents.select{|document| document.participant_id == self.id && document.nature == 'dir_resig'}
end
private
def existing_identifier?
participant_type = self.__metadata.key.to_sym
identifiers = book.send(participant_type).map{ |p| p.identifier if p.id != self.id }.compact
identifiers.include? self.identifier
end
def generate_identifier
self.identifier = self.name.parameterize if self.identifier.blank?
i = 1
while existing_identifier?
self.identifier = "#{self.identifier}-#{i}"
i += 1
end
end
def registered_on_date
unless registered_on.nil? || retired_on.nil?
if registered_on > retired_on
errors.add(:registered_on, I18n.t("mongoid.errors.models.participant.attributes.registered_on.greater_than_retired_on"))
end
end
end
def delete_objects_without_file
self.participant_files.each do |pf|
pf.delete if pf.pdf_file.file.nil?
end
end
end```

How can you clone records and their relationships in rails?

I have an App with og_objects, og_actions, and stories
I have created a way to create a clone of this app.
I am now trying to duplicate the og_objects, og_actions and stories into the clone, but I am getting stuck.
I am getting stuck in two places.
1. When I create a clone, the objects,actions,stories get moved to the
new clone, but they are not duplicated, meaning the parent app loses
them.
2. I get an error that my new clone does not have og_objects property. Specifically the error is:
ActiveModel::MissingAttributeError (can't write unknown attribute `og_actions'):
app/models/app.rb:39:in `block in populate'
app/models/app.rb:36:in `populate'
app/models/app_generator.rb:15:in `generate'
app/controllers/apps_controller.rb:12:in `create'
This is the code I have:
class App < ActiveRecord::Base
validates :name, :presence => true, :uniqueness => {:scope => :dev_mode}
validates :user_id, :presence => true
before_validation :validate_app
belongs_to :user
has_many :og_actions
has_many :og_objects
has_many :stories
has_many :log_reports
has_many :clones, class_name: "App", foreign_key: "parent_id"
belongs_to :parent_app, class_name: "App"
def self.dev_modes
['development', 'stage', 'production']
end
def is_clone?
!!self.parent_id
end
def clone_ids
if !is_clone?
self.clones.all.map(&id)
end
end
def populate
p "doing populate"
p self.clones
p "original clones"
new_og_actions = self.og_actions.dup
new_og_objects = self.og_objects.dup
new_stories = self.stories.dup
self.clones.each do |c|
p "updating ->"
p c
c[:og_actions] = new_og_actions
c[:og_objects] = new_og_objects
c[:stories] = new_stories
end
end
#Other irrelevant code here:
In my controller, I have a generator, and I have the following code:
if #parent_id.length > 0
parent_app = App.find(#parent_id)
App.create_with(og_actions: parent_app.og_actions.dup, og_objects: parent_app.og_objects.dup, stories:parent_app.stories.dup, parent_id: #parent_id, user_id: #user_id, app_id:#app_id, app_secret: #app_secret).find_or_create_by!(name: #name, dev_mode: 'clone')
parent_app.populate
else
To get this to work, I made use of the first_or_initialize method, and created the relationships if I couldn't find a record, otherwise I updated the record.
I changed the populate method to the following:
def populate
p "doing populate"
p self.clones
p "original clones"
new_og_objects = self.og_objects.dup
new_stories = self.stories.dup
self.clones.each do |c|
p "updating ->"
self.og_actions.each do | oa |
new_og_actions = OgAction.create(oa.attributes.merge({app_id:c.id, id: nil }))
c.og_actions.first_or_initialize(new_og_actions)
end
self.og_objects.each do |oo|
new_og_objects = OgObject.create(oo.attributes.merge({app_id:c.id, id: nil }))
c.og_objects.first_or_initialize(new_og_objects)
end
self.stories.each do | s|
new_stories = Story.create(s.attributes.merge({app_id:c.id, id: nil }))
c.stories.first_or_initialize(new_stories)
end
p c
end
end
I also removed the creation of these symbols in the AppGenerator like this:
if #parent_id.length > 0
parent_app = App.find(#parent_id)
App.create_with(parent_id: #parent_id, user_id: #user_id, app_id:#app_id, app_secret: #app_secret).find_or_create_by!(name: #name, dev_mode: 'clone')
parent_app.populate
else

NameError: uninitialized constant Shipment with Rspec

I'm trying to write rspec test cases with rspec-mock. But, I got an error as follow. order_decorator_spec.rb:149 is let!(:order) { create(:order, shipments: [shipment]) }. It doesn't show any stacktrace perhaps bacause it is executed on RubyMine console. How can I fix the problem??
console
NameError: uninitialized constant Shipment
./spec/models/spree/order_decorator_spec.rb:149:in `block (3 levels) in <top (required)>'
order_decorator_spec.rb
require 'spec_helper'
describe Spree::Order do
context '#after_cancel' do
let!(:shipment) { double(:shipment, :cancel! => '')}
before {shipment}
let!(:order) { create(:order, shipments: [shipment]) }
before do
order.stub(:after_cancel)
end
it "should adsfasdfasdfa" do
shipment.should_receive(:cancel!).once
order.after_cancel
end
end
end
order.rb
require 'spree/core/validators/email'
require 'spree/order/checkout'
module Spree
class Order < ActiveRecord::Base
include Checkout
checkout_flow do
go_to_state :address
go_to_state :delivery
go_to_state :payment, if: ->(order) {
order.update_totals
order.payment_required?
}
go_to_state :confirm, if: ->(order) { order.confirmation_required? }
go_to_state :complete
remove_transition from: :delivery, to: :confirm
end
token_resource
attr_reader :coupon_code
if Spree.user_class
belongs_to :user, class_name: Spree.user_class.to_s
belongs_to :created_by, class_name: Spree.user_class.to_s
else
belongs_to :user
belongs_to :created_by
end
belongs_to :bill_address, foreign_key: :bill_address_id, class_name: 'Spree::Address'
alias_attribute :billing_address, :bill_address
belongs_to :ship_address, foreign_key: :ship_address_id, class_name: 'Spree::Address'
alias_attribute :shipping_address, :ship_address
has_many :state_changes, as: :stateful
has_many :line_items, -> { order('created_at ASC') }, dependent: :destroy
has_many :payments, dependent: :destroy
has_many :return_authorizations, dependent: :destroy
has_many :adjustments, -> { order("#{Adjustment.table_name}.created_at ASC") }, as: :adjustable, dependent: :destroy
has_many :line_item_adjustments, through: :line_items, source: :adjustments
has_many :all_adjustments, class_name: 'Spree::Adjustment'
has_many :inventory_units
has_many :shipments, dependent: :destroy do
def states
pluck(:state).uniq
end
end
accepts_nested_attributes_for :line_items
accepts_nested_attributes_for :bill_address
accepts_nested_attributes_for :ship_address
accepts_nested_attributes_for :payments
accepts_nested_attributes_for :shipments
# Needs to happen before save_permalink is called
before_validation :set_currency
before_validation :generate_order_number, on: :create
before_validation :clone_billing_address, if: :use_billing?
attr_accessor :use_billing
before_create :link_by_email
after_create :create_tax_charge!
validates :email, presence: true, if: :require_email
validates :email, email: true, if: :require_email, allow_blank: true
validate :has_available_shipment
validate :has_available_payment
make_permalink field: :number
class_attribute :update_hooks
self.update_hooks = Set.new
def self.by_number(number)
where(number: number)
end
def self.between(start_date, end_date)
where(created_at: start_date..end_date)
end
def self.by_customer(customer)
joins(:user).where("#{Spree.user_class.table_name}.email" => customer)
end
def self.by_state(state)
where(state: state)
end
def self.complete
where.not(completed_at: nil)
end
def self.incomplete
where(completed_at: nil)
end
# Use this method in other gems that wish to register their own custom logic
# that should be called after Order#update
def self.register_update_hook(hook)
self.update_hooks.add(hook)
end
# For compatiblity with Calculator::PriceSack
def amount
line_items.inject(0.0) { |sum, li| sum + li.amount }
end
def currency
self[:currency] || Spree::Config[:currency]
end
def display_outstanding_balance
Spree::Money.new(outstanding_balance, { currency: currency })
end
def display_item_total
Spree::Money.new(item_total, { currency: currency })
end
def display_adjustment_total
Spree::Money.new(adjustment_total, { currency: currency })
end
def display_tax_total
Spree::Money.new(tax_total, { currency: currency })
end
def display_ship_total
Spree::Money.new(ship_total, { currency: currency })
end
def display_total
Spree::Money.new(total, { currency: currency })
end
def to_param
number.to_s.to_url.upcase
end
def completed?
completed_at.present? || complete?
end
# Indicates whether or not the user is allowed to proceed to checkout.
# Currently this is implemented as a check for whether or not there is at
# least one LineItem in the Order. Feel free to override this logic in your
# own application if you require additional steps before allowing a checkout.
def checkout_allowed?
line_items.count > 0
end
# Is this a free order in which case the payment step should be skipped
def payment_required?
total.to_f > 0.0
end
# If true, causes the confirmation step to happen during the checkout process
def confirmation_required?
Spree::Config[:always_include_confirm_step] ||
payments.valid.map(&:payment_method).compact.any?(&:payment_profiles_supported?) ||
# Little hacky fix for #4117
# If this wasn't here, order would transition to address state on confirm failure
# because there would be no valid payments any more.
state == 'confirm'
end
# Indicates the number of items in the order
def item_count
line_items.inject(0) { |sum, li| sum + li.quantity }
end
def backordered?
shipments.any?(&:backordered?)
end
# Returns the relevant zone (if any) to be used for taxation purposes.
# Uses default tax zone unless there is a specific match
def tax_zone
Zone.match(tax_address) || Zone.default_tax
end
# Indicates whether tax should be backed out of the price calcualtions in
# cases where prices include tax but the customer is not required to pay
# taxes in that case.
def exclude_tax?
return false unless Spree::Config[:prices_inc_tax]
return tax_zone != Zone.default_tax
end
# Returns the address for taxation based on configuration
def tax_address
Spree::Config[:tax_using_ship_address] ? ship_address : bill_address
end
# Array of totals grouped by Adjustment#label. Useful for displaying line item
# adjustments on an invoice. For example, you can display tax breakout for
# cases where tax is included in price.
def line_item_adjustment_totals
Hash[self.line_item_adjustments.eligible.group_by(&:label).map do |label, adjustments|
total = adjustments.sum(&:amount)
[label, Spree::Money.new(total, { currency: currency })]
end]
end
def updater
#updater ||= OrderUpdater.new(self)
end
def update!
updater.update
end
def update_totals
updater.update_totals
end
def clone_billing_address
if bill_address and self.ship_address.nil?
self.ship_address = bill_address.clone
else
self.ship_address.attributes = bill_address.attributes.except('id', 'updated_at', 'created_at')
end
true
end
def allow_cancel?
return false unless completed? and state != 'canceled'
shipment_state.nil? || %w{ready backorder pending}.include?(shipment_state)
end
def awaiting_returns?
return_authorizations.any? { |return_authorization| return_authorization.authorized? }
end
def contents
#contents ||= Spree::OrderContents.new(self)
end
# Associates the specified user with the order.
def associate_user!(user)
self.user = user
self.email = user.email
self.created_by = user if self.created_by.blank?
if persisted?
# immediately persist the changes we just made, but don't use save since we might have an invalid address associated
self.class.unscoped.where(id: id).update_all(email: user.email, user_id: user.id, created_by_id: self.created_by_id)
end
end
# FIXME refactor this method and implement validation using validates_* utilities
def generate_order_number
record = true
while record
random = "R#{Array.new(9){rand(9)}.join}"
record = self.class.where(number: random).first
end
self.number = random if self.number.blank?
self.number
end
def shipped_shipments
shipments.shipped
end
def contains?(variant)
find_line_item_by_variant(variant).present?
end
def quantity_of(variant)
line_item = find_line_item_by_variant(variant)
line_item ? line_item.quantity : 0
end
def find_line_item_by_variant(variant)
line_items.detect { |line_item| line_item.variant_id == variant.id }
end
def ship_total
adjustments.shipping.sum(:amount)
end
# Creates new tax charges if there are any applicable rates. If prices already
# include taxes then price adjustments are created instead.
def create_tax_charge!
Spree::TaxRate.adjust(self)
end
def outstanding_balance
total - payment_total
end
def outstanding_balance?
self.outstanding_balance != 0
end
def name
if (address = bill_address || ship_address)
"#{address.firstname} #{address.lastname}"
end
end
def can_ship?
self.complete? || self.resumed? || self.awaiting_return? || self.returned?
end
def credit_cards
credit_card_ids = payments.from_credit_card.pluck(:source_id).uniq
CreditCard.where(id: credit_card_ids)
end
# Finalizes an in progress order after checkout is complete.
# Called after transition to complete state when payments will have been processed
def finalize!
touch :completed_at
# lock all adjustments (coupon promotions, etc.)
adjustments.update_all state: 'closed'
# update payment and shipment(s) states, and save
updater.update_payment_state
shipments.each do |shipment|
shipment.update!(self)
shipment.finalize!
end
updater.update_shipment_state
save
updater.run_hooks
deliver_order_confirmation_email
end
def deliver_order_confirmation_email
begin
OrderMailer.confirm_email(self.id).deliver
rescue Exception => e
logger.error("#{e.class.name}: #{e.message}")
logger.error(e.backtrace * "\n")
end
end
# Helper methods for checkout steps
def paid?
payment_state == 'paid' || payment_state == 'credit_owed'
end
def available_payment_methods
#available_payment_methods ||= (PaymentMethod.available(:front_end) + PaymentMethod.available(:both)).uniq
end
def pending_payments
payments.select(&:checkout?)
end
# processes any pending payments and must return a boolean as it's
# return value is used by the checkout state_machine to determine
# success or failure of the 'complete' event for the order
#
# Returns:
# - true if all pending_payments processed successfully
# - true if a payment failed, ie. raised a GatewayError
# which gets rescued and converted to TRUE when
# :allow_checkout_gateway_error is set to true
# - false if a payment failed, ie. raised a GatewayError
# which gets rescued and converted to FALSE when
# :allow_checkout_on_gateway_error is set to false
#
def process_payments!
if pending_payments.empty?
raise Core::GatewayError.new Spree.t(:no_pending_payments)
else
pending_payments.each do |payment|
break if payment_total >= total
payment.process!
if payment.completed?
self.payment_total += payment.amount
end
end
end
rescue Core::GatewayError => e
result = !!Spree::Config[:allow_checkout_on_gateway_error]
errors.add(:base, e.message) and return result
end
def billing_firstname
bill_address.try(:firstname)
end
def billing_lastname
bill_address.try(:lastname)
end
def products
line_items.map(&:product)
end
def variants
line_items.map(&:variant)
end
def insufficient_stock_lines
line_items.select &:insufficient_stock?
end
def merge!(order, user = nil)
order.line_items.each do |line_item|
next unless line_item.currency == currency
current_line_item = self.line_items.find_by(variant: line_item.variant)
if current_line_item
current_line_item.quantity += line_item.quantity
current_line_item.save
else
line_item.order_id = self.id
line_item.save
end
end
self.associate_user!(user) if !self.user && !user.blank?
# So that the destroy doesn't take out line items which may have been re-assigned
order.line_items.reload
order.destroy
end
(snip)
(snip)
(snip)
(snip)
(snip)
(snip)
(snip)
(snip)
end
end
shipment_decorator.rb
require 'ostruct'
module Spree
Shipment.class_eval do
def after_ship
inventory_units.each &:ship!
adjustment.finalize!
#send_shipped_email
touch :shipped_at
end
end
end
shipment.rb
require 'ostruct'
module Spree
class Shipment < ActiveRecord::Base
belongs_to :order, class_name: 'Spree::Order', touch: true
belongs_to :address, class_name: 'Spree::Address'
belongs_to :stock_location, class_name: 'Spree::StockLocation'
has_many :shipping_rates, dependent: :delete_all
has_many :shipping_methods, through: :shipping_rates
has_many :state_changes, as: :stateful
has_many :inventory_units, dependent: :delete_all
has_one :adjustment, as: :source, dependent: :destroy
after_save :ensure_correct_adjustment, :update_order
attr_accessor :special_instructions
accepts_nested_attributes_for :address
accepts_nested_attributes_for :inventory_units
make_permalink field: :number, length: 11, prefix: 'H'
scope :shipped, -> { with_state('shipped') }
scope :ready, -> { with_state('ready') }
scope :pending, -> { with_state('pending') }
scope :with_state, ->(*s) { where(state: s) }
scope :trackable, -> { where("tracking IS NOT NULL AND tracking != ''") }
# shipment state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
state_machine initial: :pending, use_transactions: false do
event :ready do
transition from: :pending, to: :ready, if: lambda { |shipment|
# Fix for #2040
shipment.determine_state(shipment.order) == 'ready'
}
end
event :pend do
transition from: :ready, to: :pending
end
event :ship do
transition from: :ready, to: :shipped
end
after_transition to: :shipped, do: :after_ship
event :cancel do
transition to: :canceled, from: [:pending, :ready]
end
after_transition to: :canceled, do: :after_cancel
event :resume do
transition from: :canceled, to: :ready, if: lambda { |shipment|
shipment.determine_state(shipment.order) == :ready
}
transition from: :canceled, to: :pending, if: lambda { |shipment|
shipment.determine_state(shipment.order) == :ready
}
transition from: :canceled, to: :pending
end
after_transition from: :canceled, to: [:pending, :ready], do: :after_resume
end
def to_param
number
end
def backordered?
inventory_units.any? { |inventory_unit| inventory_unit.backordered? }
end
def shipped=(value)
return unless value == '1' && shipped_at.nil?
self.shipped_at = Time.now
end
def shipping_method
selected_shipping_rate.try(:shipping_method) || shipping_rates.first.try(:shipping_method)
end
def add_shipping_method(shipping_method, selected = false)
shipping_rates.create(shipping_method: shipping_method, selected: selected)
end
def selected_shipping_rate
shipping_rates.where(selected: true).first
end
def selected_shipping_rate_id
selected_shipping_rate.try(:id)
end
def selected_shipping_rate_id=(id)
shipping_rates.update_all(selected: false)
shipping_rates.update(id, selected: true)
self.save!
end
def refresh_rates
return shipping_rates if shipped?
return [] unless can_get_rates?
# StockEstimator.new assigment below will replace the current shipping_method
original_shipping_method_id = shipping_method.try(:id)
self.shipping_rates = Stock::Estimator.new(order).shipping_rates(to_package)
if shipping_method
selected_rate = shipping_rates.detect { |rate|
rate.shipping_method_id == original_shipping_method_id
}
self.selected_shipping_rate_id = selected_rate.id if selected_rate
end
shipping_rates
end
def currency
order ? order.currency : Spree::Config[:currency]
end
# The adjustment amount associated with this shipment (if any.) Returns only the first adjustment to match
# the shipment but there should never really be more than one.
def cost
adjustment ? adjustment.amount : 0
end
alias_method :amount, :cost
def display_cost
Spree::Money.new(cost, { currency: currency })
end
alias_method :display_amount, :display_cost
def item_cost
line_items.map(&:amount).sum
end
def display_item_cost
Spree::Money.new(item_cost, { currency: currency })
end
def total_cost
cost + item_cost
end
def display_total_cost
Spree::Money.new(total_cost, { currency: currency })
end
def editable_by?(user)
!shipped?
end
def manifest
inventory_units.group_by(&:variant).map do |variant, units|
states = {}
units.group_by(&:state).each { |state, iu| states[state] = iu.count }
OpenStruct.new(variant: variant, quantity: units.length, states: states)
end
end
def line_items
if order.complete? and Spree::Config.track_inventory_levels
order.line_items.select { |li| !li.should_track_inventory? || inventory_units.pluck(:variant_id).include?(li.variant_id) }
else
order.line_items
end
end
def finalize!
InventoryUnit.finalize_units!(inventory_units)
manifest.each { |item| manifest_unstock(item) }
end
def after_cancel
manifest.each { |item| manifest_restock(item) }
end
def after_resume
manifest.each { |item| manifest_unstock(item) }
end
# Updates various aspects of the Shipment while bypassing any callbacks. Note that this method takes an explicit reference to the
# Order object. This is necessary because the association actually has a stale (and unsaved) copy of the Order and so it will not
# yield the correct results.
def update!(order)
old_state = state
new_state = determine_state(order)
update_column :state, new_state
after_ship if new_state == 'shipped' and old_state != 'shipped'
end
# Determines the appropriate +state+ according to the following logic:
#
# pending unless order is complete and +order.payment_state+ is +paid+
# shipped if already shipped (ie. does not change the state)
# ready all other cases
def determine_state(order)
return 'canceled' if order.canceled?
return 'pending' unless order.can_ship?
return 'pending' if inventory_units.any? &:backordered?
return 'shipped' if state == 'shipped'
order.paid? ? 'ready' : 'pending'
end
def tracking_url
#tracking_url ||= shipping_method.build_tracking_url(tracking)
end
def include?(variant)
inventory_units_for(variant).present?
end
def inventory_units_for(variant)
inventory_units.group_by(&:variant_id)[variant.id] || []
end
def to_package
package = Stock::Package.new(stock_location, order)
inventory_units.includes(:variant).each do |inventory_unit|
package.add inventory_unit.variant, 1, inventory_unit.state_name
end
package
end
def set_up_inventory(state, variant, order)
self.inventory_units.create(variant_id: variant.id, state: state, order_id: order.id)
end
private
def manifest_unstock(item)
stock_location.unstock item.variant, item.quantity, self
end
def manifest_restock(item)
if item.states["on_hand"].to_i > 0
stock_location.restock item.variant, item.states["on_hand"], self
end
if item.states["backordered"].to_i > 0
stock_location.restock_backordered item.variant, item.states["backordered"]
end
end
def description_for_shipping_charge
"#{Spree.t(:shipping)} (#{shipping_method.name})"
end
def after_ship
inventory_units.each &:ship!
adjustment.finalize!
send_shipped_email
touch :shipped_at
end
def send_shipped_email
ShipmentMailer.shipped_email(self.id).deliver
end
def ensure_correct_adjustment
if adjustment
adjustment.originator = shipping_method
adjustment.label = shipping_method.adjustment_label
adjustment.amount = selected_shipping_rate.cost if adjustment.open?
adjustment.save!
adjustment.reload
elsif selected_shipping_rate_id
shipping_method.create_adjustment shipping_method.adjustment_label, order, self, true, "open"
reload #ensure adjustment is present on later saves
end
end
def update_order
order.update!
end
def can_get_rates?
order.ship_address && order.ship_address.valid?
end
end
end

undefined method `sent_messages'

When I go to /messages?mailbox=sent I get a "undefined method `sent_messages'" In the messages controller I have under def index #user.sent_messages. So I am not sure as to why it is undefined.
message model:
attr_accessible :subject, :body, :sender_id, :recepient_id, :read_at,:sender_deleted,:recepient_deleted
validates_presence_of :subject, :message => "Please enter message title"
belongs_to :sender,
:class_name => 'User',
:foreign_key => 'sender_id'
belongs_to :recepient,
:class_name => 'User',
:foreign_key => 'recepient_id'
# marks a message as deleted by either the sender or the recepient, which ever the user that was passed is.
# When both sender and recepient marks it deleted, it is destroyed.
def mark_message_deleted(id,user_id)
self.sender_deleted = true if self.sender_id == user_id and self.id=id
self.recepient_deleted = true if self.recepient_id == user_id and self.id=id
self.sender_deleted && self.recepient_deleted ? self.destroy : save!
end
# Read message and if it is read by recepient then mark it is read
def self.readingmessage(id, reader)
message = find(id, :conditions => ["sender_id = ? OR recepient_id = ?", reader, reader])
if message.read_at.nil? && (message.recepient.user_id==reader)
message.read_at = Time.now
message.save!
end
message
end
# Based on if a message has been read by it's recepient returns true or false.
def read?
self.read_at.nil? ? false : true
end
user model:
def unread_messages?
unread_message_count > 0 ? true : false
end
# Returns the number of unread messages for this user
def unread_message_count
eval 'messages.count(:conditions => ["recepient_id = ? AND read_at IS NULL", self.user_id])'
end
def to_s; username
end
def has_role?(role_name)
role.present? && role.to_sym == role_name.to_sym
end
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
I think you don't have sent_messages method in User model. Define below method in your user.rb.
def sent_messages
#Your code
end
Could you provide exactly what you looking for? So it is easy to help and you can get more appropriate answer.
EDIT:
As I see you don't have any instance level method called sent_messages in User model.

Bulk insert using one model

I'm trying to create a form using textarea and a submit button that will allow users to do bulk insert. For example, the input would look like this:
0001;MR A
0002;MR B
The result would look like this:
mysql> select * from members;
+------+------+------+
| id | no | name |
+------+------+------+
| 1 | 0001 | MR A |
+------+------+------+
| 2 | 0002 | MR B |
+------+------+------+
I'm very new to Rails and I'm not sure on how to proceed with this one. Should I use attr_accessor? How do I handle failed validations in the form view? Is there any example? Thanks in advance.
Update
Based on MissingHandle's comment, I created a Scaffold and replace the Model's code with this:
class MemberBulk < ActiveRecord::Base
attr_accessor :member
def self.columns
#columsn ||= []
end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end
column :data, :text
validates :data, :create_members, :presence => true
def create_members
rows = self.data.split("\r\n")
#member = Array.new
rows.each_with_index { |row, i|
rows[i] = row.strip
cols = row.split(";")
p = Member.new
p.no = cols[0]
p.name = cols[1]
if p.valid?
member << p
else
p.errors.map { |k, v| errors.add(:data, "\"#{row}\" #{v}") }
end
}
end
def create_or_update
member.each { |p|
p.save
}
end
end
I know the code is far from complete, but I need to know is this the correct way to do it?
class MemberBulk < ActiveRecord::Base
#Tells Rails this is not actually tied to a database table
# or is it self.abstract_class = true
# or #abstract_class = true
# ?
abstract_class = true
# members holds array of members to be saved
# submitted_text is the data submitted in the form for a bulk update
attr_accessor :members, :submitted_text
attr_accessible :submitted_text
before_validation :build_members_from_text
def build_members_from_text
self.members = []
submitted_text.each_line("\r\n") do |member_as_text|
member_as_array = member_as_text.split(";")
self.members << Member.new(:number => member_as_array[0], :name => member_as_array[1])
end
end
def valid?
self.members.all?{ |m| m.valid? }
end
def save
self.members.all?{ |m| m.save }
end
end
class Member < ActiveRecord::Base
validates :number, :presence => true, :numericality => true
validates :name, :presence => true
end
So, in this code, members is an array that is a collection of the individual Member objects. And my thinking is that as much as possible, you want to hand off work to the Member class, as it is the class that will actually be tied to a database table, and on which you can expect standard rails model behavior. In order to accomplish this, I override two methods common to all ActiveRecord models: save and valid. A MemberBulk will only be valid if all it's members are valid and it will only count as saved if all of it's members are saved. You should probably also override the errors method to return the errors of it's underlying members, possibly with an indication of which one it is in the submitted text.
In the end I had to change from using Abstract Class to Active Model (not sure why, but it stoppped working the moment I upgrade to Rails v3.1). Here's the working code:
class MemberBulk
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :input, :data
validates :input, presence: true
def initialize(attributes = {})no
attributes.each do |name, value|
send("#{name}=", value) if respond_to?("#{name}=")
end
end
def persisted?
false
end
def save
unless self.valid?
return false
end
data = Array.new
# Check for spaces
input.strip.split("\r\n").each do |i|
if i.strip.empty?
errors.add(:input, "There shouldn't be any empty lines")
end
no, nama = i.strip.split(";")
if no.nil? or nama.nil?
errors.add(:input, "#{i} doesn't have no or name")
else
no.strip!
nama.strip!
if no.empty? or nama.empty?
errors.add(:input, "#{i} doesn't have no or name")
end
end
p = Member.new(no: no, nama: nama)
if p.valid?
data << p
else
p.errors.full_messages.each do |error|
errors.add(:input, "\"#{i}\": #{error}")
end
end
end # input.strip
if errors.empty?
if data.any?
begin
data.each do |d|
d.save
end
rescue Exception => e
raise ActiveRecord::Rollback
end
else
errors.add(:input, "No data to be processed")
return false
end
else
return false
end
end # def
end

Resources