REST API Help in Rails - ruby-on-rails

I am trying to get some information posted using our accountancy package (FreeAgentCentral) using their API via a GEM.
http://github.com/aaronrussell/freeagent_api/
I have the following code to get it working (supposedly):
Kase Controller
def create
#kase = Kase.new(params[:kase])
#company = Company.find(params[:kase][:company_id])
#kase = #company.kases.create!(params[:kase])
respond_to do |format|
if #kase.save
UserMailer.deliver_makeakase("dropbox#12808311.macandco.highrisehq.com", "Highrise", #kase)
#kase.create_freeagent_project(current_user)
#flash[:notice] = 'Case was successfully created.'
flash[:notice] = fading_flash_message("Case was successfully created & sent to Highrise.", 5)
format.html { redirect_to(#kase) }
format.xml { render :xml => #kase, :status => :created, :location => #kase }
else
format.html { render :action => "new" }
format.xml { render :xml => #kase.errors, :status => :unprocessable_entity }
end
end
end
To save you looking through, the important part is:
#kase.create_freeagent_project(current_user)
Kase Model
# FreeAgent API Project Create
# Required attribues
# :contact_id
# :name
# :payment_term_in_days
# :billing_basis # must be 1, 7, 7.5, or 8
# :budget_units # must be Hours, Days, or Monetary
# :status # must be Active or Completed
def create_freeagent_project(current_user)
p = Freeagent::Project.create(
:contact_id => 0,
:name => "#{jobno} - #{highrisesubject}",
:payment_terms_in_days => 5,
:billing_basis => 1,
:budget_units => 'Hours',
:status => 'Active'
)
user = Freeagent::User.find_by_email(current_user.email)
Freeagent::Timeslip.create(
:project_id => p.id,
:user_id => user.id,
:hours => 1,
:new_task => 'Setup',
:dated_on => Time.now
)
end
lib/freeagent_api.rb
require 'rubygems'
gem 'activeresource', '< 3.0.0.beta1'
require 'active_resource'
module Freeagent
class << self
def authenticate(options)
Base.authenticate(options)
end
end
class Error < StandardError; end
class Base < ActiveResource::Base
def self.authenticate(options)
self.site = "https://#{options[:domain]}"
self.user = options[:username]
self.password = options[:password]
end
end
# Company
class Company
def self.invoice_timeline
InvoiceTimeline.find :all, :from => '/company/invoice_timeline.xml'
end
def self.tax_timeline
TaxTimeline.find :all, :from => '/company/tax_timeline.xml'
end
end
class InvoiceTimeline < Base
self.prefix = '/company/'
end
class TaxTimeline < Base
self.prefix = '/company/'
end
# Contacts
class Contact < Base
end
# Projects
class Project < Base
def invoices
Invoice.find :all, :from => "/projects/#{id}/invoices.xml"
end
def timeslips
Timeslip.find :all, :from => "/projects/#{id}/timeslips.xml"
end
end
# Tasks - Complete
class Task < Base
self.prefix = '/projects/:project_id/'
end
# Invoices - Complete
class Invoice < Base
def mark_as_draft
connection.put("/invoices/#{id}/mark_as_draft.xml", encode, self.class.headers).tap do |response|
load_attributes_from_response(response)
end
end
def mark_as_sent
connection.put("/invoices/#{id}/mark_as_sent.xml", encode, self.class.headers).tap do |response|
load_attributes_from_response(response)
end
end
def mark_as_cancelled
connection.put("/invoices/#{id}/mark_as_cancelled.xml", encode, self.class.headers).tap do |response|
load_attributes_from_response(response)
end
end
end
# Invoice items - Complete
class InvoiceItem < Base
self.prefix = '/invoices/:invoice_id/'
end
# Timeslips
class Timeslip < Base
def self.find(*arguments)
scope = arguments.slice!(0)
options = arguments.slice!(0) || {}
if options[:params] && options[:params][:from] && options[:params][:to]
options[:params][:view] = options[:params][:from]+'_'+options[:params][:to]
options[:params].delete(:from)
options[:params].delete(:to)
end
case scope
when :all then find_every(options)
when :first then find_every(options).first
when :last then find_every(options).last
when :one then find_one(options)
else find_single(scope, options)
end
end
end
# Users
class User < Base
self.prefix = '/company/'
def self.find_by_email(email)
users = User.find :all
users.each do |u|
u.email == email ? (return u) : next
end
raise Error, "No user matches that email!"
end
end
end
config/initializers/freeagent.rb
Freeagent.authenticate({
:domain => 'XXXXX.freeagentcentral.com',
:username => 'XXXX#XXXXXXX.co.uk',
:password => 'XXXXXX'
})
The above render the following error when trying to create a new Case and send the details to FreeAgent:
ActiveResource::ResourceNotFound in KasesController#create
Failed with 404 Not Found
and
ActiveResource::ResourceNotFound (Failed with 404 Not Found):
app/models/kase.rb:56:in `create_freeagent_project'
app/controllers/kases_controller.rb:96:in `create'
app/controllers/kases_controller.rb:93:in `create'
Rendered rescues/_trace (176.5ms)
Rendered rescues/_request_and_response (1.1ms)
Rendering rescues/layout (internal_server_error)
If anyone can shed any light on this problem it would be greatly appreciated!
Thanks,
Danny

How are you calling create? With a normal restful create action it would be with a POST from a form or something, but 404s are generally rendered from a failed GET action, where an ActiveRecord find fails to locate a record with a specific id. My best guess is that you're calling create with a GET, and that the line
user = Freeagent::User.find_by_email(current_user.email)
simply cannot locate a user with that email, and so is throwing the ResourceNotFound exception.
Additionally, this bit of code is confusing to me:
#kase = Kase.new(params[:kase])
#company = Company.find(params[:kase][:company_id])
#kase = #company.kases.create!(params[:kase])
respond_to do |format|
if #kase.save
Why are you creating #kase twice here, once with Kase.new and once with kases.create? Also, note that the line:
if #kase.save
will always evaluate true, because the line:
#company.kases.create!(params[:kase])
would have thrown an exception if it were false, which is another way of saying that #kase.save is redundant because create! would have already persisted the new Kase record.
EDIT: What I think you meant to do was:
# this line can go #kase = Kase.new(params[:kase])
#company = Company.find(params[:kase][:company_id])
#kase = #company.kases.build(params[:kase])
EDIT: You probably want a new action like this:
def new
#kase = Kase.new # no params here
end
The 'new' erb template will have a form_for something like:
<% form_for #kase do |k| %>
etc. That form will by default post the params from the form to the create action, assuming you've set up something like resources :kase in your routes. That should get you started. Follow the standard tutorials like you're doing and things should get simpler as you go.

Related

Can't get users to create a custom message with devise invitable

I have 2 user-like models in my app: 'Participant' and 'Member'.
I'm trying to allow them to include a custom message when they invite other members/participants through Devise Invitable. However, I can't make it work.
I'm following this official tutorial so I've made the following changes to override Devise Invitable Controller but when using pry it seems that this custom controller goes untouched when sending an invite. What am I doing wrong:
controllers/participants/invitations_controller.rb
class Participants::InvitationsController < Devise::InvitationsController
before_action :update_sanitized_params, only: :update
def create
binding.pry
#from = params[:from]
#subject = params[:invite_subject]
#content = params[:invite_content]
#participant = Participant.invite!(params[:user], current_member) do |u| #XXX Check if :user should be changed
u.skip_invitation = true
end
ParticipantInvitationNotificationMailer.invite_message(#participant, #from, #subject, #content).deliver if #participant.errors.empty?
#participant.invitation_sent_at = Time.now.utc # mark invitation as delivered
if #participant.errors.empty?
flash[:notice] = "successfully sent invite to #{#participant.email}"
respond_with #participant, :location => root_path
else
render :new
end
end
def update
respond_to do |format|
format.js do
invitation_token = Devise.token_generator.digest(resource_class, :invitation_token, update_resource_params[:invitation_token])
self.resource = resource_class.where(invitation_token: invitation_token).first
resource.skip_password = true
resource.update_attributes update_resource_params.except(:invitation_token)
end
format.html do
super
end
end
end
protected
def update_sanitized_params
devise_parameter_sanitizer.permit(:accept_invitation, keys: [:password, :password_confirmation, :invitation_token, :username])
end
end
config/routes.rb
Rails.application.routes.draw do
devise_for :members, controllers: { invitations: "members/invitations" }
devise_for :participants, controllers: { invitations: "participants/invitations" }
end
models/participant.rb
class Participant < ApplicationRecord
attr_reader :raw_invitation_token
end
mailers/notification_mailer.rb
class NotificationMailer < ApplicationMailer
def invite_message(user, from, subject, content)
#user = user
#token = user.raw_invitation_token
invitation_link = accept_user_invitation_url(:invitation_token => #token)
mail(:from => from, :bcc => from, :to => #user.email, :subject => subject) do |format|
content = content.gsub '{{first_name}}', user.first_name
content = content.gsub '{{last_name}}', user.last_name
content = content.gsub '{{full_name}}', user.full_name
content = content.gsub('{{invitation_link}}', invitation_link)
format.text do
render :text => content
end
end
end
end
If I send an invitation:with Participant.invite!({:email => 'example#email.com'}, Member.first) the invitation is sent through the default mailer as shown in the console but not through my new mailer. why?
Rendering /Users/andres/.rvm/gems/ruby-2.4.0#pixiebob/gems/devise_invitable-1.7.1/app/views/devise/mailer/invitation_instructions.html.erb
Rendered /Users/andres/.rvm/gems/ruby-2.4.0#pixiebob/gems/devise_invitable-1.7.1/app/views/devise/mailer/invitation_instructions.html.erb (0.6ms)
Rendering /Users/andres/.rvm/gems/ruby-2.4.0#pixiebob/gems/devise_invitable-1.7.1/app/views/devise/mailer/invitation_instructions.text.erb
Rendered /Users/andres/.rvm/gems/ruby-2.4.0#pixiebob/gems/devise_invitable-1.7.1/app/views/devise/mailer/invitation_instructions.text.erb (0.8ms)
Finally, I could solve this issue.
It ended up being a rookie mistake I was thinking that calling the invite! method would have anything to do with the custom create method in the custom invitations controller.
I had of course to reach the create method through the specified route and within that method prevent the invite! method to send the email through the default mailer using code below (as established clearly in the Devise Invitable Documentation):
#participant = Participant.invite!({:email => #invitation_draft.email}, current_member) do |u|
u.skip_invitation = true
end
After this we can call any custom mailer in the create method.

In Rails How to display errors in my comment form after I submit it?

I have a very straight-forward task to fulfil --- just to be able to write comments under posts and if the comments fail validation display error messages on the page.
My comment model uses a gem called Acts_as_commentable_with_threading, which creates a comment model after I installed.
On my post page, the logic goes like this:
Posts#show => display post and a form to enter comments => after the comment is entered, redisplay the Post#show page which has the new comment if it passes validation, otherwise display the error messages above the form.
However with my current code I can't display error messages if the comment validation fails. I think it is because when I redisplay the page it builds a new comment so the old one was erased. But I don't know how to make it work.
My codes are like this:
Comment.rb:
class Comment < ActiveRecord::Base
include Humanizer
require_human_on :create
acts_as_nested_set :scope => [:commentable_id, :commentable_type]
validates :body, :presence => true
validates :first_name, :presence => true
validates :last_name, :presence => true
# NOTE: install the acts_as_votable plugin if you
# want user to vote on the quality of comments.
#acts_as_votable
belongs_to :commentable, :polymorphic => true
# NOTE: Comments belong to a user
belongs_to :user
# Helper class method that allows you to build a comment
# by passing a commentable object, a user (could be nil), and comment text
# example in readme
def self.build_from(obj, user_id, comment, first_name, last_name)
new \
:commentable => obj,
:body => comment,
:user_id => user_id,
:first_name => first_name,
:last_name => last_name
end
end
PostController.rb:
class PostsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
def show
#post = Post.friendly.find(params[:id])
#new_comment = Comment.build_from(#post, nil, "", "", "")
end
end
CommentsController:
class CommentsController < ApplicationController
def create
#comment = build_comment(comment_params)
respond_to do |format|
if #comment.save
make_child_comment
format.html
format.json { redirect_to(:back, :notice => 'Comment was successfully added.')}
else
format.html
format.json { redirect_to(:back, :flash => {:error => #comment.errors}) }
end
end
end
private
def comment_params
params.require(:comment).permit(:user, :first_name, :last_name, :body, :commentable_id, :commentable_type, :comment_id,
:humanizer_answer, :humanizer_question_id)
end
def commentable_type
comment_params[:commentable_type]
end
def commentable_id
comment_params[:commentable_id]
end
def comment_id
comment_params[:comment_id]
end
def body
comment_params[:body]
end
def make_child_comment
return "" if comment_id.blank?
parent_comment = Comment.find comment_id
#comment.move_to_child_of(parent_comment)
end
def build_comment(comment_params)
if current_user.nil?
user_id = nil
first_name = comment_params[:first_name]
last_name = comment_params[:last_name]
else
user_id = current_user.id
first_name = current_user.first_name
last_name = current_user.last_name
end
commentable = commentable_type.constantize.find(commentable_id)
Comment.build_from(commentable, user_id, comment_params[:body],
first_name, last_name)
end
end
comments/form: (this is on the Posts#show page)
<%= form_for #new_comment do |f| %>
<% if #new_comment.errors.any? %>
<div id="errors">
<h2><%= pluralize(#new_comment.errors.count, "error") %> encountered, please check your input.</h2>
<ul>
<% #new_comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% end %>
I would instead use nested routes to create a more restful and less tangled setup:
concerns :commentable do
resources :comments, only: [:create]
end
resources :posts, concerns: :commentable
This will give you a route POST /posts/1/comments to create a comment.
In your controller the first thing you want to do is figure out what the parent of the comment is:
class CommentsController < ApplicationController
before_action :set_commentable
private
def set_commentable
if params[:post_id]
#commentable = Post.find(params[:post_id])
end
end
end
This means that we no longer need to pass the commentable as form parameters. Its also eliminates this unsafe construct:
commentable = commentable_type.constantize.find(commentable_id)
Where a malicous user could potentially pass any class name as commentable_type and you would let them find it in the DB... Never trust user input to the point where you use it to execute any kind of code!
With that we can start building our create action:
class CommentsController < ApplicationController
before_action :set_commentable
def create
#comment = #commentable.comments.new(comment_params) do |comment|
if current_user
comment.user = current_user
comment.first_name = current_user.first_name
comment.last_name = current_user.last_name
end
end
if #comment.save
respond_to do |format|
format.json { head :created, location: #comment }
format.html { redirect_to #commentable, success: 'Comment created' }
end
else
respond_to do |format|
format.html { render :new }
format.json { render json: #comment.errors, status: 422 }
end
end
end
private
# ...
def comment_params
params.require(:comment).permit(:first_name, :last_name, :body, :humanizer_answer, :humanizer_question_id)
end
end
In Rails when the user submits a form you do not redirect the user back to the form - instead you re-render the form and send it as a response.
While you could have your CommentsController render the show view of whatever the commentable is it will be quite brittle and may not even provide a good user experience since the user will see the top of the post they where commenting. Instead we would render app/views/comments/new.html.erb which should just contain the form.
Also pay attention to how we are responding. You should generally avoid using redirect_to :back since it relies on the client sending the HTTP_REFERRER header with the request. Many clients do not send this!
Instead use redirect_to #commentable or whatever resource you are creating.
In your original code you have totally mixed up JSON and HTML responses.
When responding with JSON you do not redirect or send flash messages.
If a JSON POST request is successful you would either:
Respond with HTTP 201 - CREATED and a location header which contains the url to the newly created resource. This is preferred when using SPA's like Ember or Angular.
Respond with HTTP 200 - OK and the resource as JSON in the response body. This is often done in legacy API's.
If it fails do to validations you should respond with 422 - Unprocessable Entity - usually the errors are rendered as JSON in the response body as well.
Added.
You can scrap your Comment.build_from method as well which does you no good at all and is very idiosyncratic Ruby.
class PostsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
def show
#post = Post.friendly.find(params[:id])
#new_comment = #post.comments.new
end
end
Don't use line contiuation (\) syntax like that - use parens.
Don't:
new \
:commentable => obj,
:body => comment,
:user_id => user_id,
:first_name => first_name,
:last_name => last_name
Do:
new(
foo: a,
bar: b
)
Added 2
When using form_for with nested resources you pass it like this:
<%= form_for([commentable, comment]) do |f| %>
<% end %>
This will create the correct url for the action attribute and bind the form to the comment object. This uses locals to make it resuable so you would render the partial like so:
I'm assuming your form_for submits a POST request which triggers the HTML format in CommentsController#create:
def create
#comment = build_comment(comment_params)
respond_to do |format|
if #comment.save
make_child_comment
format.html
format.json { redirect_to(:back, :notice => 'Comment was successfully added.')}
else
format.html
format.json { redirect_to(:back, :flash => {:error => #comment.errors}) }
end
end
end
So, if #comment.save fails, and this is an HTML request, the #create method renders create.html. I think you want to render Posts#show instead.
Keep in mind that if validations fail on an object (Either by calling save/create, or validate/valid?), the #comment object will be populated with errors. In other words calling #comment.errors returns the relevant errors if validation fails. This is how your form is able to display the errors in #new_comment.errors.
For consistency, you'll need to rename #new_comment as #comment in the posts#show action, otherwise you'll get a NoMethodError on Nil::NilClass.
TL;DR: You're not rendering your form again with your failed #comment object if creation of that comment fails. Rename to #comment in posts, and render controller: :posts, action: :show if #comment.save fails from CommentsController#create
I have figured out the answer myself with the help of others here.
The reason is that I messed up with the JSON format and html format (typical noobie error)
To be able to display the errors using the code I need to change two places ( and change #comment to #new_comment as per #Anthony's advice).
1.
routes.rb:
resources :comments, defaults: { format: 'html' } # I set it as 'json' before
2.
CommentsController.rb:
def create
#new_comment = build_comment(comment_params)
respond_to do |format|
if #new_comment.save
make_child_comment
format.html { redirect_to(:back, :notice => 'Comment was successfully added.') }
else
commentable = commentable_type.constantize.find(commentable_id)
format.html { render template: 'posts/show', locals: {:#post => commentable} }
format.json { render json: #new_comment.errors }
end
end
end

TokBox Controller / Model Ruby

UPDATE: I have solved the NilClass issue! Thanks!
Now I am having a problem with:
unknown attribute 'sessionId' for Room.
SOLVEDI am currently having some issues where my code is telling me I have an error of "undefined method `create_session' for nil:NilClass" on line 9. I will provide the files.
This is the specific line:
#new_room = Room.new(strong_param)
rooms_controller.rb
class RoomsController < ApplicationController
require "opentok"
before_filter :config_opentok,:except => [:index]
def index
#rooms = Room.where(:public => true).order("created_at DESC")
#new_room = Room.new
end
def create
session = #opentok.create_session :media_mode => :routed
params[:room][:sessionId] = session.session_id
#new_room = Room.new(strong_param)
respond_to do |format|
if #new_room.save
format.html { redirect_to(“/party/”+#new_room.id.to_s) }
else
format.html { render :controller => ‘rooms’, :action => “index” }
end
end
end
def party
#room = Room.find(params[:id])
#tok_token = #opentok.generate_token #room.sessionId
end
private
def config_opentok
if #opentok.nil?
#opentok = OpenTok::OpenTok.new ########, "#########################################"
end
end
def strong_param
params.require(:room).permit(:name,:sessionId)
end
end
rooms.rb (Models)
class Room < ActiveRecord::Base
end
I've tried several different modifications to these files to make my program work. I can get the listing page to work but once I try and actually create a new room, I receive this error message.
Look forward to any advice you can provide.
You are missing the before_filter :config_opentok,:except => [:index] line from the blog post in your previous post (https://railsfornovice.wordpress.com/2013/01/01/video-chatting-in-ruby-on-rails/)

How To Create Action Items For Specific Customer In Rails

New to Rails. New to OOP. I have a client and action_item model. An action item (a todo) has many and belongs to many clients. A client, has many action items. Essentially: A user, creates TODO's, from client pages.
User: creates a client (Crayola LLC, for ex) with crud.
User is then on the Client's show page (Crayola LLC's show page).
My question is, HOW TO have: User to be able to create an action item, for that client. Example: Call Crayola, to sell them an upgrade).
Created join table called action_items_clients, with foreign keys client_id, and action_item_id. Ran migration. Just have no idea how to facilitate creation of action items FOR clients. As it stands, action items can be created without clients. That's simple crud. This is where my novice understanding of rails hits roadblocks.
Action Items Controller:
class ActionItemsController < ApplicationController
def index
#action_items = ActionItem.all
end
def new
#action_items = ActionItem.new
end
def create
#action_item = ActionItem.new(action_items_params)
if #action_item.save
redirect_to(:action => 'show', :id => #action_item.id)
#renders client individual page
else
redirect_to(:action => 'new')
end
end
def edit
#action_item = ActionItem.find(params[:id])
end
def update
#action_item = ActionItem.find(params[:id])
if #action_item.update_attributes(action_items_params)
redirect_to(:controller => 'action_items', :action => 'show', :id => #action_item.id)
flash[:notice] = "Updated"
else
render 'new'
end
end
def show
#action_item = ActionItem.find(params[:id])
end
def action_clients
#action_clients = ActionItem.Client.new
end
def delete
#action_items = ActionItem.find(params[:id])
end
def destroy
#action_items = ActionItem.find(params[:id]).destroy
redirect_to(:controller => 'action_items', :action => 'index')
end
private
def action_items_params
params.require(:action_item).permit(:purpose, :correspondence_method, :know_person, :contact_name_answer, :additional_notes)
end
end
Clients controller
class ClientsController < ApplicationController
def index
#clients = Client.all
end
def new
#client = Client.new
end
def create
#client = Client.new(clients_params)
if #client.save
redirect_to(:action => 'show', :id => #client.id)
#renders client individual page
else
redirect_to(:action => 'new')
end
end
def edit
#client = Client.find(params[:id])
end
def update
#client = Client.find(params[:id])
if #client.update_attributes(clients_params)
redirect_to(:action => 'show', :id => #client.id)
else
render 'edit'
end
end
def show
#client = Client.find(params[:id])
end
def delete
#client = Client.find(params[:id])
end
def destroy
#client = Client.find(params[:id]).destroy
redirect_to(:controller => 'clients', :action => 'index')
end
private
def clients_params
params.require(:client).permit(:name)
end
end
Show page for each client:
<div align="center"><h1> <%= #client.name %> </h1></div>
<ol><li><%= link_to('Enter Definition Mode', :controller => 'action_items', :action => 'new', :id => #client.id) %></br></br></li>
<li><%= link_to('Back to client List', :controller => 'clients', :action => 'index') %> </li></br>
</ol>
The way I would do this is setup your routes so that action_items are nested under the client, something like so:
# /clients/13/action_items
resources :clients do
resources :action_items
end
Or if the user logging in is a client or only has one client, then you could skip that, and just have resources :action_items.
Then if you direct a user to /clients/13/action_items, then they will hit action_items#index, and params[:client_id] will be set to 13. You can use this to scope the action_items throughout that controller.
As long as you have the relationships setup between Client and ActionItem setup:
class Client < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :action_items
end
class ActionItem < ActiveRecord::Base
has_and_belongs_to_many :clients
end
It is probably also good to scope that to the currently logged in user:
class User < ActiveRecord::Base
has_many :clients
end
but it depends on how you want things to work. This is probably how I'd structure things:
class ActionItemsController < ApplicationController
before_action :get_client
def index
#action_items = #client.action_items.all
end
def new
#action_items = #client.action_items.new
end
def create
#action_item = #client.action_items.new(action_items_params)
if #action_item.save
redirect_to(:action => 'show', :id => #action_item.id, :client_id => #client.id)
else
redirect_to(:action => 'new')
end
end
# and other actions....
private
def get_client
#client = current_user.clients.find(params[:client_id])
end
end
EDIT (to address some commented questions):
If the action_items aren't always scoped to a client, they can live under both a nested and an un-nested route at the same time:
# /action_items
resources :action_items
resources :clients do
# /clients/13/action_items
resources :action_items
end
Then the before_action can be a bit more generic to set the owner to either the client, or the user itself (as long as User also has_and_belongs_to_many :action_items):
class ActionItemsController < ApplicationController
before_action :get_owner
def index
#action_items = #owner.action_items.all
end
# ... other stuff
private
def get_owner
if params[:client_id].present?
#owner = current_user.clients.find(params[:client_id])
else
#owner = current_user
end
end
end
Your redirects will probably need to take into account whether they came from a nested page or not, so you might have some logic like this around them:
def destroy
item = #owner.action_items.find(params[:id])
item.destroy
if params[:client_id]
redirect_to client_action_items_path(params[:client_id])
else
redirect_to action_items_path
end
end
Your link_tos will also have to change similarly, here's a link to the above destroy action:
<% if params[:client_id].present? %>
<%= link_to 'Delete action item', client_action_item_path(params[:client_id], #action_item), :method => 'delete' %>
<% else %>
<%= link_to 'Delete action item', #action_item, :method => 'delete' %>
<% end %>

Unable to access controller variables in model

I have following controller
class PaypalOrdersController < Spree::BaseController
def new
#paypal_order = PaypalOrder.new
end
def create
#order1 = current_order
#paypal_order = PaypalOrder.new(params[:paypal_order])
if #paypal_order.save
if #paypal_order.purchase
render :action => "success"
else
render :action => "failure"
end
else
render :action => "new"
end
end
end
and the corresponding model is:
class PaypalOrder < ActiveRecord::Base
require 'paypal_payment'
belongs_to :order
attr_accessor :card_number, :card_verification
validate :validate_card, :on => :create
def purchase
orderHash = #order1.clone
paypalHash = #paypal_order.clone
PaypalPayment.new(orderHash, paypalHash)
end
private
def validate_card
#some code
end
def credit_card
#some code
end
end
When the purchase method is triggered I'm getting the error cannot clone nil class. On debugging I found that #order1 and #paypal_order both are nil in the purchase method. I am not sure why this is happening. Please can someone explain.
Thanks
controller:
PaypalOrder.purchase #order1, #paypal_order
model:
def self.purchase order, paypal_order
orderHash = order.clone
paypalHash = paypal_order.clone
PaypalPayment.new(orderHash, paypalHash)
end
edit:
How do you pass data from a controller to a model with Ruby on Rails?
Try this
controller
if #paypal_order.purchase #order1
model
def purchase order1
orderHash = order1.clone
paypalHash = self.clone
PaypalPayment.new(orderHash, paypalHash)
end

Resources