passing an id in Rails 4 - ruby-on-rails-4

If i start this question by showing you my routes: -
c:\Sites\work\easygifts>rake routes
Prefix Verb URI Pattern Controller#Action
writing_stores GET /stores/writing(.:format) stores#writing
office_stores GET /stores/office(.:format) stores#office
time_stores GET /stores/time(.:format) stores#time
home_stores GET /stores/home(.:format) stores#home
wellness_stores GET /stores/wellness(.:format) stores#wellness
travel_stores GET /stores/travel(.:format) stores#travel
bags_stores GET /stores/bags(.:format) stores#bags
leisure_stores GET /stores/leisure(.:format) stores#leisure
quote_stores GET /stores/quote(.:format) stores#quote
stores GET /stores(.:format) stores#index
POST /stores(.:format) stores#create
new_store GET /stores/new(.:format) stores#new
edit_store GET /stores/:id/edit(.:format) stores#edit
store GET /stores/:id(.:format) stores#show
PATCH /stores/:id(.:format) stores#update
PUT /stores/:id(.:format) stores#update
DELETE /stores/:id(.:format) stores#destroy
products GET /products(.:format) products#index
POST /products(.:format) products#create
new_product GET /products/new(.:format) products#new
edit_product GET /products/:id/edit(.:format) products#edit
product GET /products/:id(.:format) products#show
PATCH /products/:id(.:format) products#update
PUT /products/:id(.:format) products#update
DELETE /products/:id(.:format) products#destroy
root GET / stores#index
The issue i am having is getting the :id into the 'quote' view.
I am wanting to see in my routes; quote_stores GET /stores/quote/:id(.:format) stores#quote Or something like it.
Can :id only be passed through CRUD?? I thought i could pass instance variables through pretty much anywhere so I wrote this in my view as the link to the view with the :id info passed into it.
<% #products.each do |office| %>
<div class="item">
<%= link_to image_tag(office.image_url), image_path(office.image_url), class: 'fancybox' %>
<p><strong><%= office.item_code%></strong>
</br><em><%= truncate(office.title, length: 18) %></em></p>
<p class="showArticle"><%= link_to 'Show Article', store_path(office) %></p>
<p class="addTo"><%= link_to 'Price Info', quote_stores_path(office) %></p>
</div>
<% end %>
I am referring to the <%= link_to 'Price Info', quote_stores_path(office) %> which upon click takes you to the correct view and in the URI path it even lists the correct :id however it does not pass into the view that :id's information.
My controller code is as follows: -
class StoresController < ApplicationController
add_breadcrumb 'home', :stores_path
def index
#products = Product.all
end
def show
#products = Product.find(params[:id])
if #products.nil?
redirect_to action: :index
end
add_breadcrumb 'Back', #products.section
end
def writing
#products = Product.where(:section => 'writing').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'writing', writing_stores_path
end
def office
#products = Product.where(:section => 'office').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'office', office_stores_path
end
def time
#products = Product.where(:section => 'time').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'time', time_stores_path
end
def home
#products = Product.where(:section => 'home').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'home', home_stores_path
end
def wellness
#products = Product.where(:section => 'wellness').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'wellness', wellness_stores_path
end
def travel
#products = Product.where(:section => 'travel').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'travel', travel_stores_path
end
def bags
#products = Product.where(:section => 'bags').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'bags', bags_stores_path
end
def leisure
#products = Product.where(:section => 'leisure').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'leisure', leisure_stores_path
end
def quote
#products = Product.find_by(params[:id])
if #products.nil?
redirect_to action: :index
end
end
end
So apart from my code not being DRY, what am i missing here please? What am i not understanding about :id's?

In your config/routes.rb, you probably have this line:
resources :stores
Which creates routes for the standard CRUD actions of the stores resource. You can define additional actions for this resource which either apply to the collection (multiple products) or the members individually (a single product).
Please note that it would probably more logical to name the resource products rather than stores, since it seems to be handling Products.
In your case, you'd want to define an additional member action. Since it applies to a single product, Rails will define a route which takes an id parameter:
resources :stores do
member do
get 'quote'
end
end
This will generate the following route (rake routes):
quote_store GET /stores/:id/quote(.:format) stores#quote
Note that the route is called quote_store, the singular form rather than the plural.
Also see the Rails guides for more information about collection and member routes.

Related

Strong parameters with Rails 4.0 and Devise

I am using Rails 4.2 and devise.Request type will always be JSON.My registrations controller looks like this
class Users::RegistrationsController < Devise::RegistrationsController
before_filter :configure_sign_up_params, only: [:create]
def create
if request.format(request) == "application/json"
build_resource(sign_up_params)
if resource.save
render :json => {:success => "true", :message => "Thank You For Registering."}
else
render :json => {:success => "false", :message => resource.errors.full_messages}
end
else
super
end
end
protected
def configure_sign_up_params
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:email, :password, :password_confirmation)
end
end
end
The parameters that show-up in the console is like :
Parameters: {"email"=>"test123#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "registration"=>{"email"=>"test123#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}
The error it produces is that the controller doesn't recognize the email and password fields and always returns error like :
["Email can't be blank","Password can't be blank"]
Does this happen because of any foolishness that I have written in the code?Please help
Your configure_sign_up_params method has to be something like this to work.
def configure_sign_up_params
devise_parameter_sanitizer.for(:registration) do |u|
u.permit(:email, :password, :password_confirmation)
end
end
If you can see the console content what you have posted, it contains a hash with registration as a key:
"registration"=>{"email"=>"test123#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}

Carrierwave + ActiveAdmin with example for file and image field. results in "stack level too deep"

I am attempting to create a file and image field on ActiveAdmin. Example.
What I have so far.
Created a Migration to add file and image columns to the database.
Created Active Admin and edited display form.
Created the model
Created 2 uploaders
My code results in "stack level too deep" error.
The weird thing is the code is exactly the same as my example which works fine.
admin/product.rb
ActiveAdmin.register Product do
permit_params :title, :image, :file
form(:html => { :multipart => true }) do |f|
f.inputs "Create Product..." do
f.input :title
f.input :image, :as => :file, :hint => f.template.image_tag(f.object.image.url(:thumb))
f.input :file, :as => :file
end
f.actions
end
end
models/product.rb
class Product < ActiveRecord::Base
mount_uploader :file, FileProductsUploader
mount_uploader :image, ImageProductsUploader
end
uploaders/file_products_uploader.rb
class FileProductsUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"#{Rails.root}/public/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
uploaders/image_products_uploader.rb
class ImageProductsUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :file
def store_dir
"#{Rails.root}/public/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :thumb do
process :resize_to_fit => [250, 250]
end
end
Error Message
SystemStackError in Admin::ProductsController#update
stack level too deep
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"ViiAmqH+S9XjP0wBSc7I2USEl1LXLw/N532Kd+uhNqc=",
"product"=>{"title"=>"First Product",
"image"=>#<ActionDispatch::Http::UploadedFile:0x50e6008 #tempfile=# <Tempfile:C:/Users/User/AppData/Local/Temp/RackMultipart20141016-7388-e5v6is>,
#original_filename="example.png",
#content_type="image/png",
#headers="Content-Disposition: form-data; name=\"product[image]\"; filename=\"example.png\"\r\nContent-Type: image/png\r\n">},
"commit"=>"Update Product",
"id"=>"1"}
Console Error message
Unpermitted parameters: utf8, _method, authenticity_token, commit, id
Completed 500 Internal Server Error in 138ms
SystemStackError (stack level too deep):
actionpack (4.1.5) lib/action_dispatch/middleware/reloader.rb:79
None of the online solutions worked for me. I solved the error changing the local temp directory
Uploader/*
def cache_dir
# should return path to cache dir
Rails.root.join 'tmp/uploads'
end

Mongoid Model won't persist has_one via embeds_one relation

I'm having trouble with embeds in a mongoid4-based rails 4 app. I've been looking for an answer everywhere for the past 2 days. So here is the code.
This is a church management app, with a Service model, embedding a team and a band. Each team/band has several roles such as "presidence", "communion" that refer to a user.
My models :
class Service
include Mongoid::Document
...
embeds_one :team, autobuild: true
embeds_one :band, autobuild: true
...
accepts_nested_attributes_for :team, :band
end
class Team
include Mongoid::Document
embedded_in :service
has_one :presidence, :class_name => 'User', autosave: true
has_one :message, :class_name => 'User', autosave: true
...
end
class Band
include Mongoid::Document
has_one :lead, :class_name => 'User', autosave: true
has_one :guitar, :class_name => 'User', autosave: true
...
embedded_in :service
end
class User
include Mongoid::Document
embeds_one :profile
belongs_to :team, :inverse_of => :presidence
belongs_to :team, :inverse_of => :message
belongs_to :band, :inverse_of => :lead
belongs_to :band, :inverse_of => :guitar
def fullname
"#{profile.firstname} #{profile.lastname}"
end
def self.find_by_talent(talent)
self.where("profile.talents.name" => talent)
end
end
The services controller :
# POST /services
# POST /services.json
def create
#service = Service.new(service_params)
respond_to do |format|
if #service.save
format.html { redirect_to #service, notice: 'Service was successfully created.' }
format.json { render action: 'show', status: :created, location: #service }
else
format.html { render action: 'new' }
format.json { render json: #service.errors, status: :unprocessable_entity }
end
end
end
...
def service_params
params.require(:service).permit(:date, :time, :place, :name, :theme, :team => [:id, :precedence, :message ], :band => [:id, :lead, :guitar ])
end
And the form in _form.html.erb :
<%= form_for(#service) do |f| %>
...
<%= f.fields_for #service.team do |tf| %>
<%= tf.collection_select :presidence, User.find_by_talent(:presidence), :_id, :fullname, {:include_blank => "select a person"} %>
<%= tf.collection_select :message, User.find_by_talent(:message), :id, :fullname, {:include_blank => "select a person"} %>
<% end %>
<%= f.fields_for #service.band do |bf| %>
<%= bf.collection_select :lead, User.find_by_talent(:lead), :id, :fullname, {:include_blank => "select a person"} %>
<%= bf.collection_select :guitar, User.find_by_talent(:guitar), :id, :fullname, {:include_blank => "select a person"} %>
<% end %>
...
<% end %>
When creating a service, everything seems to run fine, but this is what I get in the console :
2.0.0-p195 :001 > s = Service.last
=> #<Service _id: 52ea18834d61631e7e020000, date: "2014-02-02", time: "10:00", place: "Where it's at", name: "My great name", theme: "The service's theme">
2.0.0-p195 :002 > s.team
=> #<Team _id: 52ea18834d61631e7e030000, >
2.0.0-p195 :003 > s.team.presidence
=> nil
Why is s.team.presidence not created ? s.team looks weird, too, with an ending comma...
Here is the content of my rails log :
Started POST "/services" for 127.0.0.1 at 2014-01-30 10:16:51 +0100
Processing by ServicesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Ph6lbdHC2FbiANn/fGSzHWprenj3fWKXM40Hrsc5+AM=", "service"=>{"date"=>"2014-02-02", "name"=>"My great name", "theme"=>"The service's theme", "time"=>"10:00", "place"=>"Where it's at", "team"=>{"presidence"=>"52ea18324d61631e81010000", "message"=>"52ea18324d61631e81010000"}, "band"=>{"lead"=>"52ea18324d61631e81010000", "guitar"=>"52ea18324d61631e81010000"}}, "commit"=>"Create Service"}
MOPED: 127.0.0.1:27017 COMMAND database=admin command={:ismaster=>1} runtime: 0.6610ms
MOPED: 127.0.0.1:27017 UPDATE database=service_boot_camp_development collection=users selector={"band_id"=>BSON::ObjectId('52ea18834d61631e7e010000'), "_id"=>{"$nin"=>[]}} update={"$set"=>{"band_id"=>nil}} flags=[:multi]
COMMAND database=service_boot_camp_development command={:getlasterror=>1, :w=>1} runtime: 0.5800ms
MOPED: 127.0.0.1:27017 INSERT database=service_boot_camp_development collection=services documents=[{"_id"=>BSON::ObjectId('52ea18834d61631e7e020000'), "date"=>"2014-02-02", "time"=>"10:00", "place"=>"Where it's at", "name"=>"My great name", "theme"=>"The service's theme", "team"=>{"_id"=>BSON::ObjectId('52ea18834d61631e7e030000')}, "band"=>{"_id"=>BSON::ObjectId('52ea18834d61631e7e010000')}}] flags=[]
COMMAND database=service_boot_camp_development command={:getlasterror=>1, :w=>1} runtime: 2.7460ms
I guess I'm doing something wrong, but I have no clue if it is in the database model or in the form... or anything else...
You will not be able to do it this way. When you create an embedded document, its _id and all of its data are embedded directly within the parent document. This is in contrast to an association, where the document with the belongs_to gets a foreign key which points to its associated parent document. So here, your User documents each have a team_id and band_id, but when the database tries to get the documents, it can't find them, since you can't query directly for embedded documents; you need the parent document first. For more, see the Mongoid documentation.
Another potential issue is that you have multiple belongs_to definitions in the User models. This will also cause an issue, because for each one of those, Mongoid will attempt to create a team_id and band_id. You should name them separately and specify a class name; perhaps names like :presiding_team and :message_team, :lead_band and :guitar_band, etc. This answer should show you what that would look like.
I would recommend making the Team and Band separate referenced documents instead of embedded documents, since you won't be able to reference users effectively while they're embedded.
Hope this helps.

Strong params rails 4 permitting params

Here is what my params look like,
{
:project_id => "1",
:ticket => {:name => "Ticket1"}
}
How do I add the project_id to the permitted params?
Controller
params.require(:ticket).permit(:name)
Here is what I did
I added another function for project params
def ticket_group_params
params.require(:ticket_group).permit(:name)
end
def project_params
params.permit(:project_id)
end
Then I just merge the results only for the create action eg.
# POST /ticket_group
# POST /ticket_group.json
def create
#ticket_group = TicketGroup.new(ticket_group_params.merge(project_params))
respond_to do |format|
def ticket_params
name_params = params.require(:ticket).permit(:name)
name_params.merge(params.permit(:project_id))
end
# => {name: 'val', project_id: 'val'}
or
def ticket_params
params.require(:ticket).permit(:name).tap do |_params|
_params[:project_id] = params.permit(:project_id)
end
end
# => {name: 'val', project_id: 'val'}
or, it may be
def ticket_params
params.permit(:project_id, ticket: [:name])
end
# => {project_id: 'val', ticket: {name: 'val'}}
You likely need to add this as a hidden attribute on your form, like so:
<input type="hidden" name="ticket[project_id]" value="<%= #ticket.project_id %>" />
Then in the controller you can do this:
params.require(:ticket).permit(:name,:project_id)
Here is a helpful link:
https://github.com/rails/strong_parameters

Why am I getting "Can't mass-assign protected attributes" after adding a field to a product on spree?

I'm trying to add a field to products on spree which is just a checkbox which is just ment to mark products if they are for sale or they're internal products.
I've added the migration and finally figured out how to add the checkbox on the form, but when I click Update I get Can't mass-assign protected attributes: for_sale
This is the migration
class AddProductForSaleField < ActiveRecord::Migration
def up
add_column :spree_products, :for_sale, :boolean
end
def down
remove_column :spree_products, :for_sale
end
end
Here's the field being added
Deface::Override.new(:virtual_path => "spree/admin/products/_form",
:name => "for_sale",
:insert_before => "code[erb-silent]:contains('track_inventory_levels')",
:partial => "spree/admin/products/for_sale")
And this is the partial
<%= f.field_container :for_sale do %>
<%= f.label :for_sale, t(:for_sale) %>
<%= f.check_box :for_sale, { :checked => true } %>
<% end %>
got it, was missing the model part
Spree::Product.class_eval do
attr_accessible :for_sale
end
Mass Assignment is the name Rails gives to the act of constructing your object with a parameters hash. It is "mass assignment" in that you are assigning multiple values to attributes via a single assignment operator.
The following snippets perform mass assignment of the name and topic attribute of the Post model:
Post.new(:name => "John", :topic => "Something")
Post.create(:name => "John", :topic => "Something")
Post.update_attributes(:name => "John", :topic => "Something")
In order for this to work, your model must allow mass assignments for each attribute in the hash you're passing in.
There are two situations in which this will fail:
You have an attr_accessible declaration which does not include :name
You have an attr_protected which does include :name
It recently became the default that attributes had to be manually white-listed via a attr_accessible in order for mass assignment to succeed. Prior to this, the default was for attributes to be assignable unless they were explicitly black-listed attr_protected or any other attribute was white-listed with attr_acessible.
If it's a permission issue then you can add :
Spree::Product.class_eval do
attr_accessible :variable_1, :variable_2 :as => [:default, :product]
end
Marking it as default for a specific model, will remove the mass-assignment warning message !