Ruby on Rails: Calling Model method in view - ruby-on-rails

I am trying to achieve Paypal integration with rails. Following this (http://railscasts.com/episodes/141-paypal-basics) I have a function in model which call paypal service with a return url. I have added a link in view that links to method in model.But some how rails is not able to get function in model.
What am i doing wrong?
My View :
form_for #order do |f|
- if #order.errors.any?
#error_explanation
h2 = "#{pluralize(#order.errors.count, "error")} prohibited this order from being saved:"
ul
- #order.errors.full_messages.each do |message|
li = message
.field
= f.label :first_name
= f.text_field :first_name
.field
= f.label :last_name
= f.text_field :last_name
.field
= f.label :card_number
= f.text_field :card_number
.field
= f.label :card_verification, "Card Verification Value (CVV)"
= f.text_field :card_verification
.field
= f.label :card_expires_on
= f.date_select :card_expires_on, {start_year: Date.today.year, end_year: (Date.today.year+10), add_month_numbers: true, discard_day: true}, {class: "browser-default"}
.field
= link_to 'PayPal', #order.paypal_url(current_user)<= link to model function
.actions
= f.submit
My Model : defined following function.
def paypal_url(return_url)
values = {
:business => 'xxxxx.XXXXX#techflex.com',
:cmd => '_cart',
:upload => 1,
:return => return_url,
:invoice => id
}
values.merge!({
"amount_#{1}" => item.unit_price,
"item_name_#{1}" => item.product.name,
"item_number_#{1}" => item.id,
"quantity_#{1}" => item.quantity
})
"https://www.sandbox.paypal.com/cgi-bin/webscr?" + values.to_query
end
Error :
NoMethodError in Orders#new
Showing C:/Users/Suniljadhav/SourceCode/TrainStation/app/views/orders/_form.html.slim where line #24 raised:
private method `paypal_url' called for #<Order:0x5e49bf8>
Trace of template inclusion: app/views/orders/new.html.slim
Rails.root: C:/Users/Suniljadhav/Source Code/TrainStation

It seems that paypal_url is a private method and you are trying to call it from outside of its class.
In your Order class you probably have the keyword private. Every method below that keyword will be declared private (as opposed to public) and an error will be thrown if you try to call it from outside the class. Private methods can only be called from within the class where they are defined. So try moving the definition of paypal_url so that it appears before private in your class.
You can read more about how this works here, and about the reasons behind it here.

Related

Passing params to a partial in slim

Rails 3.2
Ruby 2.15
I have a somewhat complex view app/views/tickets/show.html.slim. Inside this view, I render various sections of the the ticket
One section is called customer_info. Here's what I have:
render 'tickets/sections/customer_info', locals: { customer_info: CustomerInfo.new, ticket: #ticket }
In my app/views/tickets/sections/_customer_info.html.slim, I have:
= form_for(customer_info) do |f|
- :ticket_id = ticket.id
= f.hidden_field :ticket_id
.form-horizontal-column.customer-info
.form-group
= f.label :first_name
= f.text_field :first_name
.form-group
= f.label :last_name
= f.text_field :last_name
.actions = f.submit 'Save'
.clear
I am however getting the following error message:
_customer_info.html.slim:2: syntax error, unexpected '=', expecting keyword_end
; :ticket_id = ticket.id;
^
I am starting to learn .slim. Any ideas?
You can't set a symbol to something - they are immutable. You can remove the 2nd line do do something like:
= form_for(customer_info) do |f|
= f.hidden_field :ticket_id, :value => ticket.id
...

Create action not being called in form

I've just started a new app where I want to take a postcode in a form and save it to the database. My problem is that the create action doesn't seem to be being called no matter what I try.
Routes:
root 'postcodes#new'
resources :postcodes, only: [:new ,:create]
Controller: postcodes_controller.rb
class PostcodesController < ApplicationController
def new
#postcode = Postcode.new
end
def create
#postcode = Postcode.new(postcode_params)
if #postcode.save
flash[:success] = 'Success'
else
flash[:error] = 'Error'
end
end
private
def postcode_params
params.require(:postcode).permit(:code)
end
end
Model: postcode.rb
class Postcode < ApplicationRecord
validates :code, presence: true, uniqueness: true
end
View: postcodes/new.haml
.container
%form
%fieldset.form-group
= form_for #postcode do |f|
= f.label :postcode
= f.text_field :code, placeholder: 'Example Postcode', class: 'form-control'
= f.submit 'Submit', class: 'btn btn-primary'
I've attempted to pass more options in the form_for such as the method and action and now I have a feeling it's a routing error.
Any help will be appreciated.
Thanks.
I believe the problem you are experiencing is a result of your HAML.
You do not need to use, nor should you use, a form HTML element outside the form_for method call.
The form_for method will handle generating this HTML element/tag for you.
You have:
.container
%form
%fieldset.form-group
= form_for #postcode do |f|
= f.label :postcode
= f.text_field :code, placeholder: 'Example Postcode', class: 'form-control'
= f.submit 'Submit', class: 'btn ban-primary'
Which outputs an empty <form> element.
You should have:
.container
= form_for #postcode do |f|
%fieldset.form-group
= f.label :postcode
= f.text_field :code, placeholder: 'Example Postcode', class: 'form-control'
= f.submit 'Submit', class: 'btn ban-primary'
That should generate a proper <form> tag with the required action and method attributes populated with the right URL and 'post' so that your create action is called.

boolean input_filed calls immediately the method on Rails

The project which I am working in, is developed on Rails using haml markup to views. There is a view with a simple form like this:
= simple_form_for #message, url: [:admin, #request, #message], html: { class: 'form vertical-form} do |f|
= f.input :text, as: :text, input_html: { class: 'form-control', rows: 5 }
%br
= f.input :link_url, input_html: { class: 'form-control'}
%br
- if #message.has_picture_image?
= f.label :image
=link_to #message.picture_image, target: "_blank" do
= image_tag #message.picture_image(:thumb)
= f.file_field :image, class:'imagen-button'
= f.input_field :remove_picture, as: :boolean, inline_label: 'Remove'
%br
.form-actions
= f.submit(t('accept'), class: 'btn btn-large btn-primary')
= link_to(t('cancel'), [:admin, #message.request, #message], class: 'btn btn-large btn-danger')
and in Message model there is the bellow method:
def remove_picture
self.picture.destroy
end
The input_field is used to check if I want to remove the message image if it exists. I understood that input_filed gives me the option to check it so that when I click on accept button, it call the method remove_picture in the Message model. But, before the browser deploys the form, it rise the next error:
undefined method `to_i' for #<Picture:0x007f7675162b58>
Extracted source (around line #39):
37: = image_tag #message.picture_image(:thumb)
38: = f.file_field :image, class:'imagen-button'
39: = f.input_field :remove_picture, as: :boolean, inline_label: 'Remove'
40: %br
41: .form-actions
42: = f.submit(t('accept'), class: 'btn btn-large btn-primary')
and if I reload the page, this time the form is deployed. I guess this is because in the first time, as the picture exists then immediatly the remove_picture is called and the picture removed, and when I reload the form, as the picture already does not exist, the form is shown.
Obviously I am undestanding wrongly the input_field usage.
SimpleForms input_field is a helper which binds an input to a model attribute. It does not create a box which calls your method when the box is ticked! But rather it will call your remove_picture method when it rendering the form.
In some cases like checkboxes you will want to bind inputs to attributes that are not saved in the database. We call these virtual attributes. They are just like any old Ruby attributes:
class Message < ActiveRecord::Base
attr_accessor :remove_picture
# since this method is destructive it should have a bang (!)
def remove_picture!
self.picture.destroy
end
end
You could use it like this:
class MessagesController < ApplicationController
def update
#message.update(update_params)
#message.remove_picture! if message.remove_picture
# ...
end
def update_params
params.require(:message).allow(:remove_picture)
end
end
But there is a better way:
class Message < ActiveRecord::Base
has_one :picture_image
accepts_nested_attributes_for :picture_image, allow_destroy: true
end
accepts_nested_attributes_for lets us create an image with picture_image_attributes and destroy an image with:
#picture.update(picture_image_attributes: { _destroy: true })
This is how we would set up the form:
= simple_form_for #message, url: [:admin, #request, #message], html: { class: 'form vertical-form} do |f|
= f.input :text, as: :text, input_html: { class: 'form-control', rows: 5 }
%br
= f.input :link_url, input_html: { class: 'form-control'}
%br
- if #message.has_picture_image?
f.simple_fields_for :picture_image do |pif|
= pif.label :image
= link_to #message.picture_image, target: "_blank" do
= image_tag #message.picture_image(:thumb)
= pif.file_field :image, class:'imagen-button'
= pif.input_field :_destroy, as: :boolean, inline_label: 'Remove'
%br
.form-actions
= f.submit(t('accept'), class: 'btn btn-large btn-primary')
= link_to(t('cancel'), [:admin, #message.request, #message], class: 'btn btn-large btn-danger')
And your strong parameters whitelist:
def update_attributes
params.require(:message).allow(
:text,
:link_url,
picture_image_attributes: [:image, :_destroy]
)
end

Rails 4 yield additional form fields

I'm trying to DRY up one of my forms that sometimes has additional fields (for nested models) when making a new entry, but wouldn't need those fields when performing an update. As such, I was trying to pass the additional fields in via a block, but the form object isn't being passed properly.
Is there a way to pass a form object (or really any variable) into a yield?
Example code for reference:
_form.slim
= nested_form_for #model do |f|
.row
= f.label :name
= f.text_field :name, autofocus: true
...
= yield
...
= f.submit 'Save'
new.html.slim
== render layout: 'model/form' do
h3 Additional Fields
= f.fields_for :nested do |h|
= a.label :name, 'Nested Name'
= a.text_field :name
= a.link_to_remove do
= fa_icon 'times-circle-o'
= f.link_to_add "Add another nested model", :nested
edit.html.slim
== render layout: 'model/form'
To elaborate on my comment, this is how I'd do it using partials:
_form.slim
= nested_form_for #model do |f|
.row
= f.label :name
= f.text_field :name, autofocus: true
...
- if defined?(additional_fields)
h3 Additional Fields
= f.fields_for :nested do |h|
= a.label :name, 'Nested Name'
= a.text_field :name
= a.link_to_remove do
= fa_icon 'times-circle-o'
= f.link_to_add "Add another nested model", :nested
...
= f.submit 'Save'
new.html.slim
== render 'model/form', :additional_fields => true
edit.html.slim
== render 'model/form'
I might be missing something, but I'm not sure why this wouldn't work.

Rails - undefined method `each' for 0:Fixnum

I get the following error in Ruby on Rails undefined method 'each' for 0:Fixnum.
Here is the application trace :
app/controllers/videos_controller.rb:23:in `new'
app/controllers/videos_controller.rb:23:in `create'
And my controller create and new actions :
def new
#video = Video.new
end
def create
method = 'get_' + params[:video][:provider] + '_video_id'
params[:video][:provider_video_id] = Video.send(method, params[:video][:url])
params[:video][:thumb] = Video.get_thumb_from_youtube(params[:video][:provider_video_id])
params[:video][:views] = params[:video][:likes] = 0
params[:video][:user_id] = current_user
#video = Video.new(params[:video])
if #video.save
redirect_to video_path(#video), notice:'Video added successfully.'
else
render :new
end
end
Here is my view.html.haml :
= form_for #video do |f|
- if #video.errors.any?
.error_explanation
%h2= pluralize(#video.errors.count, "error")
prohibited this user from being saved:
%ul
- #video.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :title
= f.text_field :title
.field
= f.label :description
= f.text_area :description
%br
.field
= f.label :url, 'URL'
= f.text_field :url
%br
.field
Provider
= radio_button :video, :provider, 'vimeo'
= f.label :provider, 'Vimeo', :value => 'vimeo'
= radio_button :video, :provider, 'youtube'
= f.label :provider, 'Youtube', :value => 'youtube'
%br
.field
Category
= collection_select(:video, :category_id, Category.all, :id, :name, :include_blank => true)
%br
.actions
= f.submit "Add video"
From
params[:video][:views] = params[:video][:likes] = 0
to
params[:video][:views] = 0
I assume that video.likes is an association, not a count, so it expects an Enumerable. If it's an association, rails tries to assign the elements you add to likes to your video model. The first step of adding them is to iterate - using each. That's where the error comes from.

Resources