undefined method `paypal_url' for nil:NilClass - ruby-on-rails

I have a model class page.rb:
class Page < ActiveRecord::Base
def paypal_url(return_url)
values = {
:business => 'seller_1229899173_biz#railscasts.com',
:cmd => '_cart',
:upload => 1,
:return => return_url,
:invoice => id
}
line_items.each_with_index do |item, index|
values.merge!({
"amount_#{index+1}" => item.unit_price,
"item_name_#{index+1}" => item.product.name,
"item_number_#{index+1}" => item.id,
"quantity_#{index+1}" => item.quantity
})
end
"https://www.sandbox.paypal.com/cgi-bin/webscr?" + values.to_query
end
end
And in my controller:
def order_form
if params[:commit] == "Proceed to Checkout"
redirect_to(#cart.paypal_url("/pages/order_online"))
end
but when I click on "Proceed to Checkout", it gives me an error:
undefined method `paypal_url' for nil:NilClass
Any idea if I'm missing anything or doing anything wrong?
Thanks!

The simple answer is your #cart variable is not being populated with any data
The way to fix this is to create a #cart variable in your order_form controller, like so:
def order_form
#cart = Cart.new cart_params #-> how to define your cart var?
redirect_to #cart.paypal_url "/pages/order_online"
end
I looked at the Railscast you've been using, and it seems he's continuing on from another episode, where he explains how to create the cart object using standard ActiveRecord
A longer answer is that I believe your system is flawed, in that you're calling PayPal methods in your Page model (why?)
We've set up our own cart in Rails before (you can see here):
Cart session model #-> stores product id's in a cart model
Product model stores the products & links with cart id's
Order controller sends data to Paypal & handles returns
Here's some code for you:
#config/routes.rb
get 'cart' => 'cart#index', :as => 'cart_index'
post 'cart/add/:id' => 'cart#add', :as => 'cart_add'
delete 'cart/remove(/:id(/:all))' => 'cart#delete', :as => 'cart_delete'
get 'checkout/paypal' => 'orders#paypal_express', :as => 'checkout'
get 'checkout/paypal/go' => 'orders#create_payment', :as => 'go_paypal'
get 'checkout/stripe' => 'orders#stripe', :as => 'stripe'
#app/models/cart_session.rb #-> "session based model"
class CartSession
#Initalize Cart Session
def initialize(session)
#session = session
#session[:cart] ||= {}
end
#Cart Count
def cart_count
if (#session[:cart][:products] && #session[:cart][:products] != {})
#session[:cart][:products].count
else
0
end
end
#Cart Contents
def cart_contents
products = #session[:cart][:products]
if (products && products != {})
#Determine Quantities
quantities = Hash[products.uniq.map {|i| [i, products.count(i)]}]
#Get products from DB
products_array = Product.find(products.uniq)
#Create Qty Array
products_new = {}
products_array.each{
|a| products_new[a] = {"qty" => quantities[a.id.to_s]}
}
#Output appended
return products_new
end
end
#Qty & Price Count
def subtotal
products = cart_contents
#Get subtotal of the cart items
subtotal = 0
unless products.blank?
products.each do |a|
subtotal += (a[0]["price"].to_f * a[1]["qty"].to_f)
end
end
return subtotal
end
#Build Hash For ActiveMerchant
def build_order
#Take cart objects & add them to items hash
products = cart_contents
#order = []
products.each do |product|
#order << {name: product[0].name, quantity: product[1]["qty"], amount: (product[0].price * 100).to_i }
end
return #order
end
#Build JSON Requests
def build_json
session = #session[:cart][:products]
json = {:subtotal => self.subtotal.to_f.round(2), :qty => self.cart_count, :items => Hash[session.uniq.map {|i| [i, session.count(i)]}]}
return json
end
end
#app/controllers/cart_controller.rb
# shows cart & allows you to click through to "buy"
class CartController < ApplicationController
include ApplicationHelper
#Index
def index
#items = cart_session.cart_contents
#shipping = Shipping.all
end
#Add
def add
session[:cart] ||={}
products = session[:cart][:products]
#If exists, add new, else create new variable
if (products && products != {})
session[:cart][:products] << params[:id]
else
session[:cart][:products] = Array(params[:id])
end
#Handle the request
respond_to do |format|
format.json { render json: cart_session.build_json }
format.html { redirect_to cart_index_path }
end
end
#Delete
def delete
session[:cart] ||={}
products = session[:cart][:products]
id = params[:id]
all = params[:all]
#Is ID present?
unless id.blank?
unless all.blank?
products.delete(params['id'])
else
products.delete_at(products.index(id) || products.length)
end
else
products.delete
end
#Handle the request
respond_to do |format|
format.json { render json: cart_session.build_json }
format.html { redirect_to cart_index_path }
end
end
end
#app/controllers/orders_controller.rb
#Paypal Express
def paypal_express
response = EXPRESS_GATEWAY.setup_purchase(total,
:items => cart_session.build_order,
:subtotal => subtotal,
:shipping => 50,
:handling => 0,
:tax => 0,
:return_url => url_for(:action => 'create_payment'),
:cancel_return_url => url_for(:controller => 'cart', :action => 'index')
)
redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
end
#Create Paypal Payment
def create_payment
response = express_purchase
#transactions.create!(:action => "purchase", :amount => ((cart_session.subtotal * 100) + 50).to_i, :response => response)
#cart.update_attribute(:purchased_at, Time.now) if response.success?
response.success?
redirect_to cart_index_path
end
#Stripe
def stripe
credit_card = ActiveMerchant::Billing::CreditCard.new(
:number => "4242424242424242",
:month => "12",
:year => "2020",
:verification_value => "411"
)
purchaseOptions = {
:billing_address => {
:name => "Buyer Name",
:address1 => "Buyer Address Line 1",
:city => "Buyer City",
:state => "Buyer State",
:zip => "Buyer Zip Code"
}
}
response = STRIPE_GATEWAY.purchase(total, credit_card, purchaseOptions)
Rails.logger.debug response.inspect
#responder = response
render cart_index_path
end
private
def subtotal
(cart_session.subtotal * 100).to_i
end
def total
((cart_session.subtotal * 100) + 50).to_i
end
def express_purchase
EXPRESS_GATEWAY.purchase(total, express_purchase_options)
end
def express_purchase_options
{
:token => params[:token],
:payer_id => params[:PayerID]
}
end
def express_token=(token)
self[:express_token] = token
if new_record? && !token.blank?
details = EXPRESS_GATEWAY.details_for(token)
self.express_payer_id = details.payer_id
self.first_name = details.params["first_name"]
self.last_name = details.params["last_name"]
end
end
end

Related

NoMethodError Building a simple CMS in Rails

I've been following along with this tutorial from lynda.com.
I'm in chapter 15: Nesting pages in subjects
So I've followed through and have even resorted to just copying the exercise files they provide you with. However I keep getting this error:
NoMethodError in PagesController#new
undefined method `id' for nil:NilClass
It hi-lights the second line:
def new
#page = Page.new({:subject_id => #subject.id, :name => "Default"})
#subjects = Subject.order('position ASC')
#page_count = Page.count + 1
end
Here's my controller
class PagesController < ApplicationController
layout "admin"
before_action :confirm_logged_in
before_action :find_subject
def index
# #pages = Page.where(:subject_id => #subject.id).sorted
#pages = #subject.pages.sorted
end
def show
#page = Page.find(params[:id])
end
def new
#page = Page.new({:subject_id => #subject.id, :name => "Default"})
#subjects = Subject.order('position ASC')
#page_count = Page.count + 1
end
def create
#page = Page.new(page_params)
if #page.save
flash[:notice] = "Page created successfully"
redirect_to(:action => 'index', :subject_id => #subject_id)
else
#subjects = Subject.order('position ASC')
#page_count = Page.count + 1
render('new')
end
end
def edit
#page = Page.find(params[:id])
#subjects = Subject.order('position ASC')
#page_count = Page.count
end
def update
# Find an existing object using form parameters
#page = Page.find(params[:id])
# Update the object
if #page.update_attributes(page_params)
# If update succeeds, redirect to the index action
flash[:notice] = "Page updated successfully"
redirect_to(:action => 'show', :id => #page.id, :subject_id => #subject_id)
else
# If the update fails, redisplay the form so user can fix problems
#subjects = Subject.order('position ASC')
#page_count = Page.count
render('edit')
end
end
def delete
#page = Page.find(params[:id])
end
def destroy
page = Page.find(params[:id]).destroy
flash[:notice] = "Page '#{page.name}' destroyed successfully"
redirect_to(:action => 'index', :subject_id => #subject_id)
end
private
def page_params
# Same as using "params[:subject]", except that it:
# - raises as error if :subject is not present
# - allows listed attributes to be mass-assigned
params.require(:page).permit(:subject_id, :name, :permalink, :position, :visible)
end
def find_subject
if params[:subject_id]
#subject = Subject.find(params[:subject_id])
end
end
end
Then here's the view:
<% #page_title = "New Page" %>
<%= link_to("<< Back to list", {:action => 'index', :subject_id => #subject_id}, :class => 'back-link') %>
<div class="pages new">
<h2>Create Page</h2>
<%= form_for(:page, :url => {:action => 'create', :subject_id => #subject_id}) do |f| %>
<%= render(:partial => "form", :locals => {:f => f}) %>
<div class="form-buttons">
<%= submit_tag("Create page") %>
</div>
<% end %>
</div>
A question similar to this one has been asked here, but I don't see any solutions there.
Any help is greatly appreciated!
I think the problem is in
Page.new({:subject_id => #subject.id, :name => "Default"})
#subject is not defined and you are asking for its id
You have to define #subject.
Put this as first line in your new action
#subject = Subject.find(params[:id])
Right now #subject is undefined therefore it's nil, calling .id on nil is resulting in the error you're seeing.

Reference message id when submitting form

When users compose a message, how can I setup so that it references the new message_id created for the conversation_id inside the Messages table?
For example User A sends a new message to User B. Message_id 26 is created and the conversation_id will be 26.
show.html.erb:
<h4>Send Message To User</h4>
<p><%= link_to "Message Me", new_user_message_path(#user), :class => "button" %>
new.html.erb:
<%= f.text_field :conversation_id %>
controller:
def index
#messages = Message.scoped
#message = Message.new
if params[:mailbox] == "sent"
#messages = #user.sent_messages.paginate :per_page => 10, :page => params[:page], :order => "created_at DESC"
elsif params[:mailbox] == "inbox"
#messages = #user.received_messages.paginate :per_page => 10, :page => params[:page], :order => "created_at DESC"
#elsif params[:mailbox] == "archived"
# #messages = #user.archived_messages
end
if params[:mailbox] == "unread"
#messages = #user.unread_messages.paginate :per_page => 10, :page => params[:page], :order => "created_at DESC"
end
if params[:mailbox] == "trash"
#messages = #user.deleted_messages.paginate :per_page => 10, :page => params[:page], :order => "created_at DESC"
end
end
def new
#message = Message.new
#message.conversation_id = params[:conversation_id]
end
def create
#message = Message.new(params[:message])
#message.sender_id = #user.id
if #message.save
flash[:notice] = "Message has been sent"
redirect_to user_messages_path(current_user, :mailbox=>:inbox)
else
render :action => :new
end
end
def show
#new_message = Message.new
#message = Message.find(params[:id])
#message.readingmessage if #message.recipient == current_user
end
def reply
#reply_message = Message.new
#message = Message.new
#message.conversation_id = params[:conversation_id]
end
def destroy
#message = Message.find(params[:id])
#message.destroy
flash[:notice] = "Successfully deleted message."
redirect_to user_messages_path(#user, #messages)
end
def delete_multiple
if params[:delete]
params[:delete].each { |id|
#message = Message.find(id)
#message.mark_message_deleted(#message.id,#user.id) unless #message.nil?
}
flash[:notice] = "Messages deleted"
end
redirect_to user_messages_path(#user, #messages)
end
def update
#message = Message.new
if params[:reply_to]
#reply_to = User.find_by_id(params[:reply_to])
unless #reply_to.nil?
#message.recipient_id = #reply_to.id
end
end
end
Model:
class Message < ActiveRecord::Base
attr_accessible :subject, :conversation_id, :body, :parent_id, :sender_id, :recipient_id, :read_at,:sender_deleted,:recipient_deleted
validates_presence_of :subject, :message => "Please enter message title"
has_many :notifications, as: :event
belongs_to :conversation, inverse_of: :messages
belongs_to :user
scope :unread, -> {where('read_at IS NULL')}
scope :not_deleted_by_recipient, where('messages.recipient_deleted IS NULL OR messages.recipient_deleted = ?', false)
scope :not_deleted_by_sender, where('messages.sender_deleted IS NULL OR messages.sender_deleted = ?', false)
belongs_to :sender,
:class_name => 'User',
:foreign_key => 'sender_id'
belongs_to :recipient,
:class_name => 'User',
:foreign_key => 'recipient_id'
after_create :set_converstation_id
def set_conversation_id
update_column :conversation_id, id
end
def reply
new_message.reply_from_user_id = self.id #save the user id of original repost, to keep track of where it originally came from
end
def self.by_date
order("created_at DESC")
end
# marks a message as deleted by either the sender or the recipient, which ever the user that was passed is.
# When both sender and recipient marks it deleted, it is destroyed.
def mark_message_deleted(id,user_id)
self.sender_deleted = true if self.sender_id == user_id
self.recipient_deleted = user_id if self.recipient_id == user_id
(self.sender_deleted > 0 && self.recipient_deleted > 0) ? self.destroy : self.save!
(self.sender_deleted != 0 && self.recipient_deleted != 0)
end
# Read message and if it is read by recipient then mark it is read
def readingmessage
self.read_at ||= Time.now
save
end
# Based on if a message has been read by it's recipient returns true or false.
def read?
self.read_at.nil? ? false : true
end
def self.received_by(user)
where(:recipient_id => user.id)
end
def self.not_recipient_deleted
where("recipient_deleted = ?", false)
end
def self.sent_by(user)
Message.where(:sender_id => user.id)
end
def next(same_recipient = true)
collection = Message.where('id <> ? AND created_at > ?', self.id, self.created_at).order('created_at ASC')
collection.where(recipient_id: self.recipient_id) if same_recipient
collection.first
end
def previous(same_recipient = true)
collection = Message.where('id <> ? AND created_at < ?', self.id, self.created_at).order('created_at DESC')
collection.where(recipient_id: self.recipient_id) if same_recipient
collection.first
end
end
private
def send_notification(message)
message.notifications.create(user: message.recipient)
end
If you don't have separate models for conversations and messages, how will you discriminate between the creation of a message and the creation of a conversation?
You should have a Conversation Model that has_many messages and references both users.
Anyways, what you're asking is to have a conversation_id when creating a brand new message (or conversation)?
You can do this in two ways:
1.- Change
#message.conversation_id = params[:conversation_id]
to
#message.conversation_id = #message.id
And in the view use a hidden_field_tag (or the helper that does that) for your conversation_id
2.- Set this id directly on the create method
#message.conversation_id = #message.id
(is that the behavior you wanted?)
Also, why do you have #new_message and #message?
Update
As said in the comments:
I have this
<%= link_to "Reply", reply_user_messages_path(#message.sender, :conversation_id => #message) %>
<%= link_to "Reply", reply_user_messages_path(#message.sender, :conversation_id => #message.conversation_id) %>
I need to find a way to merge the two of them. One method sets a conversation_id and the other copies it.
Easiest way to do that, one line if:
<%= link_to "Reply", reply_user_messages_path(#message.sender, conversation_id: #message.conversation_id ? #message.conversation_id : #message.id) %>

Rails save draft update in the controller?

I have a model called articles which has a string field that allows users to set their article to draft. When a draft is selected and a user updates the post I would like it to return to the article edit page as if the user selected the published option then I would like for the user to be redirected to the articles index page.
The problem is I cannot get the article to update and redirect back to the post if the draft option is selected. Am I approaching this the wrong way?
Migration file
def change
add_column :articles, :status, :string, default: 'Draft'
end
articles.rb
scope :submitted, lambda { where('status = ?', 2) }
scope :draft, lambda{ where('status = ?', 1) }
def is_draft?
self.draft
end
articles controller
def update
case #article.status
when 1
#article.status = 'Draft'
else 2
#article.status = 'Published'
end
if #article.status == 1
#article = article.find(params[:id])
flash[:notice] = "Successfully Updated" if #article.update_attributes(params[:article])
respond_with(#article, :location => edit_article_path)
else
#article = article.find(params[:id])
flash[:notice] = "Successfully Updated" if #article.update_attributes(params[:article])
respond_with(#article, :location => articles_path)
end
end
If you really want to work with 1/2 values
Model:
STATUS_VALUES = {1 => "Draft", 2 => "Published"}
scope :submitted, lambda { where('status = ?', STATUS_VALUES[2]) }
scope :draft, lambda{ where('status = ?', STATUS_VALUES[1]) }
attr_accessible :_status
after_initialize do
self.draft! if self.new_record? # be draft by default
end
def draft!
self.status = STATUS_VALUES[1]
end
def published!
self.status = STATUS_VALUES[2]
end
def _status
STATUS_VALUES.invert(status)
end
def _status=(value)
case value
when 1, "1" then self.draft!
when 2, "2" then self.published!
else self.draft!
end
end
def draft?
self.status == STATUS_VALUES[1]
end
def published?
self.status == STATUS_VALUES[2]
end
Controller:
def update
#article = article.find(params[:id])
if #article.update_attributes(params[:article])
flash[:notice] = "Successfully Updated"
if #article.draft?
respond_with(#article, :location => edit_article_path)
else
respond_with(#article, :location => articles_path)
end
else
render :action => :edit
end
end
View:
<%= f.check_box(:_status, "Published", 2, 1) %>

paypal recurring gem & trial period

I implemented paypal recurring by following Rails Casts EP289. http://railscasts.com/episodes/289-paypal-recurring-billing?view=asciicast
It works fine for normal process, but when I tried to implement trial period, I got some problem. It charges the sum of trial amount and recurring billing amount at the first billing.
(I only want to charge trial amount)
I checked through internet, but I can't figure out how to do that so far.
What am I missing about implementing this correctly?
Below is the code.
paypal_payment.rb
class PaypalPayment
def initialize(subscription)
#subscription = subscription
#price = Price.find_by_currency_code_and_plan_id("JPY", #subscription.plan.id)
end
def checkout_details
PayPal::Recurring.new(token: #subscription.paypal_payment_token).checkout_details
end
def checkout_url(options)
process(:checkout, options).checkout_url
end
def make_recurring
process :request_payment
process :create_recurring_profile, period: :monthly, frequency: 1, start_at: Time.now + 1.month
end
private
def process(action, options = {})
options = options.reverse_merge(
token: #subscription.paypal_payment_token,
payer_id: #subscription.paypal_customer_token,
item_name: #subscription.plan.name,
item_amount: #price.amount.to_i,
description: "Something"
amount: #price.amount.to_i,
outstanding: :next_billing,
trial_amount: #price.initial_amount.to_i,
trial_period: :monthly,
trial_frequency: 1,
trial_length: 1,
currency: #price.currency_code,
locale: "ja_JP"
)
response = PayPal::Recurring.new(options).send(action)
raise response.errors.inspect if response.errors.present?
response
end
end
subscription.rb
class Subscription < ActiveRecord::Base
belongs_to :plan
belongs_to :student
has_many :transactions, :class_name => "SubscriptionTransaction"
validates_presence_of :plan_id
validates_presence_of :student_id
attr_accessor :paypal_payment_token
attr_accessible :paypal_customer_token, :paypal_recurring_profile_token, :plan_id, :student_id, :paypal_payment_token
def save_with_payment
if valid?
if payment_provided?
save_with_paypal_payment
end
end
end
def paypal
PaypalPayment.new(self)
end
def save_with_paypal_payment
response = paypal.make_recurring
self.paypal_recurring_profile_token = response.profile_id
save!
end
def payment_provided?
paypal_payment_token.present?
end
end
subscriptions_controller.rb
require 'time'
class SubscriptionsController < ApplicationController
before_filter :authenticate_user!, :is_student?
def index
#title = I18n.t "subscriptions.index.title"
#student = Student.find_by_id(current_user.profile_id)
if #student.is_trial == true
redirect_to 'new'
end
#subscription = #student.subscription
#plan = #subscription.plan
Time.zone = "Tokyo"
#date = Time.now.in_time_zone.to_date
#total_lesson_times = Lesson.find_all_by_student_id(#student.id).size
#lastm_lesson_times = Lesson.where("student_id = :student_id AND lesson_day >= :start_date AND lesson_day <= :end_date",
{:student_id => #student.id, :start_date => #date.beginning_of_month()-1.month, :end_date => #date.end_of_month()-1.month}).size
#thism_lesson_times = Lesson.where("student_id = :student_id AND lesson_day >= :start_date AND lesson_day <= :end_date",
{:student_id => #student.id, :start_date => #date.beginning_of_month(), :end_date => #date.end_of_month()}).size
end
def new
#title = I18n.t "subscriptions.new.title"
#plans = Plan.all
#student = Student.find_by_id(current_user.profile_id)
if #student.is_trial == false && #student.is_active == false
redirect_to 'index'
end
#subscription = Subscription.new
#date = Time.now.in_time_zone.to_date
#total_lesson_times = Lesson.find_all_by_student_id(#student.id).size
#lastm_lesson_times = Lesson.where("student_id = :student_id AND lesson_day >= :start_date AND lesson_day <= :end_date",
{:student_id => #student.id, :start_date => #date.beginning_of_month()-1.month, :end_date => #date.end_of_month()-1.month}).size
#thism_lesson_times = Lesson.where("student_id = :student_id AND lesson_day >= :start_date AND lesson_day <= :end_date",
{:student_id => #student.id, :start_date => #date.beginning_of_month(), :end_date => #date.end_of_month()}).size
end
def modify
end
def cancel
student = Student.find(current_user.profile_id)
#subscription = student.subscription
ppr = PayPal::Recurring.new(:profile_id => #subscription.paypal_recurring_profile_token)
response = ppr.suspend
if response.success?
student.update_attributes(:is_active => false)
flash[:notice] = I18n.t "subscriptions.cancel.notice_flash"
redirect_to root_path
end
end
def reactivate
student = Student.find_by_id(current_user.profile_id)
#subscription = student.subscription
ppr = PayPal::Recurring.new(:profile_id => #subscription.paypal_recurring_profile_token)
response = ppr.reactivate
if response.success?
student.update_attributes(:is_active => true)
flash[:success] = I18n.t "subscriptions.reactivate.success_flash"
redirect_to root_path
end
end
def paypal_checkout
plan = Plan.find(params[:plan_id])
student = Student.find(current_user.profile_id)
subscription = plan.subscriptions.build
redirect_to subscription.paypal.checkout_url(
return_url: subscriptions_confirm_url(:plan_id => plan.id),
cancel_url: new_subscription_url
)
end
def confirm
#plan = Plan.find(params[:plan_id])
#student = Student.find(current_user.profile_id)
#subscription = #plan.subscriptions.build
if params[:PayerID]
#subscription.student_id = #student.id
#subscription.paypal_customer_token = params[:PayerID]
#subscription.paypal_payment_token = params[:token]
end
end
def complete
student = Student.find(current_user.profile_id)
Time.zone = student.user.time_zone
current_time = Time.now.in_time_zone
current_date = current_time.to_date
#subscription = Subscription.new(
:plan_id => params[:subscription][:plan_id],
:student_id => params[:subscription][:student_id],
:paypal_customer_token => params[:subscription][:paypal_customer_token],
:paypal_payment_token => params[:subscription][:paypal_payment_token]
)
if #subscription.save_with_payment
student.update_attributes(:is_active => true, :is_trial => false, :expiration_date => current_date + 1.month - 1.day)
redirect_to root_path, :notice => (I18n.t "subscriptions.complete.success_flash")
else
flash[:notice] = I18n.t "error_flash"
redirect_to new_subscription_path
end
end
private
def is_student?
if current_user.profile_type != "Student"
flash[:notice] = I18n.t "is_student_notice_flash"
redirect_to root_path
end
end
end
(Answered in a question edit. Converted to a community wiki answer. See What is the appropriate action when the answer to a question is added to the question itself? )
The OP wrote:
I solved the problem myself. The problem was inside the paypal_payment.rb
def make_recurring
process :request_payment
process :create_recurring_profile, period: :monthly, frequency: 1, start_at: Time.now + 1.month
end
because of the process :request_payment part. It charges the amount to the user beside of the charge of recurring payment.

How do I fix the fields_for error: # "is not allowed as an instance variable n" in Rails?

My goal is to enable a user to be able to submit multiple NewsImages from a parent Blog form.
My Blog model looks like this:
# == Schema Information
# Schema version: 20091006171847
#
# Table name: blogs
#
# id :integer(4) not null, primary key
# title :string(255)
# body :text
# profile_id :integer(4)
# created_at :datetime
# updated_at :datetime
#
class Blog < ActiveRecord::Base
has_many :comments, :as => :commentable, :order => "created_at asc"
has_many :news_images, :dependent => :destroy
belongs_to :profile
accepts_nested_attributes_for :news_images
validates_presence_of :title, :body
attr_immutable :id, :profile_id
def after_create
feed_item = FeedItem.create(:item => self)
([profile] + profile.friends + profile.followers).each{ |p| p.feed_items << feed_item }
end
def to_param
"#{self.id}-#{title.to_safe_uri}"
end
end
My NewsImage model looks like this:
class NewsImage < ActiveRecord::Base
belongs_to :blog
validates_each :blog do |news_image, attr, value|
news_item.errors.add attr, "You are only limited to 5 images." if news_item.blog.news_items.size >= 5
end
has_attached_file :image, :styles => { :original => "1920x1080>", :square => "158x158#", :thumb => "386x155#", :slide_thumb => "165x67#"},
:url => "/system/:attachment/:id/:style/:basename.:extension",
:path => ":rails_root/public/system/:attachment/:id/:style/:basename.:extension"
def validate
dimensions = Paperclip::Geometry.from_file(self.image.queued_for_write[:original])
self.errors.add(:image, "Please upload an image that is at least 476 pixels wide") if dimensions.width < 476
self.errors.add(:image, "Please upload an image that is at least 319 pixels high") if dimensions.height < 319
end
end
My Blog controller looks like this:
class BlogsController < ApplicationController
skip_filter :login_required, :only => [:index, :show]
prepend_before_filter :get_profile
before_filter :setup
def index
if #p && #p == #profile && #p.blogs.empty?
flash[:notice] = 'You have not create any blog posts. Try creating one now.'
redirect_to new_profile_blog_path(#p) and return
end
respond_to do |wants|
wants.html {render}
wants.rss {render :layout=>false}
end
end
def create
#blog = #p.blogs.build params[:blog]
respond_to do |wants|
if #blog.save
wants.html do
flash[:notice] = 'New blog post created.'
redirect_to profile_blogs_path(#p)
end
else
wants.html do
flash.now[:error] = 'Failed to create a new blog post.'
render :action => :new
end
end
end
end
def show
render
end
def edit
render
end
def update
respond_to do |wants|
if #blog.update_attributes(params[:blog])
wants.html do
flash[:notice]='Blog post updated.'
redirect_to profile_blogs_path(#p)
end
else
wants.html do
flash.now[:error]='Failed to update the blog post.'
render :action => :edit
end
end
end
end
def destroy
#blog.destroy
respond_to do |wants|
wants.html do
flash[:notice]='Blog post deleted.'
redirect_to profile_blogs_path(#p)
end
end
end
protected
def get_profile
#profile = Profile[params[:profile_id]]
end
def setup
#user = #profile.user
#blogs = #profile.blogs.paginate(:page => #page, :per_page => #per_page)
if params[:id]
#blog = Blog[params[:id]]
else
#blog = Blog.new
3.times {#blog.news_images.build }
end
end
def allow_to
super :owner, :all => true
super :all, :only => [:index, :show]
end
end
My form currently is this:
<%
#locals
blog ||= #blog
%>
<div id="blog">
<% less_form_for [#profile, blog], :html => {:multipart => true} do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<% f.fields_for :news_images do |builder| %>
<%= builder.label :caption %>
<%= builder.text_field :caption %>
<%= builder.label :image %>
<%= builder.file_field :image %>
<% end %>
To include a youtube video use: [youtube: address_of_video]
<div class="row button">
<%= f.submit 'Save', :class=>'button' %>
</div>
<% end %>
</div>
I keep getting the following error:
ActionView::TemplateError (`#blog[news_images_attributes][0]' is not allowed as an instance variable name) on line #12 of app/views/blogs/_form.html.erb:
If I remove:
<%= builder.text_field :caption %>
the error goes away. In fact if i change the text field to a file field the error goes away.
I'm really confused here as I'm not sure why one form helper works fine and the other doesn't.
Thank you very much for looking =)
Update:
This is my less_form_builder.rb file:
class LessFormBuilder < ActionView::Helpers::FormBuilder
include ActionView::Helpers::ActiveRecordHelper
def method_missing *args
options = args.extract_options!
label = get_label '', options
front(label) + super(*args) + back(label)
end
def wrap method, options = {}
s = front(method, options)
s += yield if block_given?
s += back(method, options)
end
# Generates a label
#
# If +options+ includes :for,
# that is used as the :for of the label. Otherwise,
# "#{this form's object name}_#{method}" is used.
#
# If +options+ includes :label,
# that value is used for the text of the label. Otherwise,
# "#{method titleized}: " is used.
def label method, options = {}
text = options.delete(:label) || "#{method.to_s.titleize}: "
if options[:for]
"<label for='#{options.delete(:for)}'>#{text}</label>"
else
#need to use InstanceTag to build the correct ID for :for
ActionView::Helpers::InstanceTag.new(#object_name, method, self, #object).to_label_tag(text, options)
end
end
def select method, options = {}
front(method, options) + super + back(method, options)
end
def text_field method, options = {}
front(method, options) + super + back(method, options)
end
def password_field method, options = {}
front(method, options) + super + back(method, options)
end
def text_area method, options = {}
front(method, options) + super + back(method, options)
end
def check_box method, options = {}
front(method, options) + super + back(method, options)
end
def calendar_field method, options = {}
expired = options.delete(:expired) || false
if not expired; options.merge!(:class => 'calendar'); else; options.merge!(:disabled => true); end
text_field method, options
end
def front method = '', options = {}
"<div class='row clear'>#{label(method, options)}"
end
def back method = '', options = {}
"#{error_messages_on( object_name, method ) unless method.blank?}
<div class='clear'></div>
</div>"
end
end

Resources