Rails clone copy or duplicate - ruby-on-rails-3

I have a nested form and once I save, I want to be able to click a link on the show page to copy or clone that form and open a new one. From there I should be able to make edits (like a new id) and save as a new record. I have seen some examples like this deep_cloneable gem, but I have no idea how to implement it. I think this should be simple, but I just don't understand where to put things in the controller and in the show view.

If you want to copy an activeRecord object you can use its attributes to create new one like
you can have an action in your controller which can be called on link,
def create_from_existing
#existing_post = Post.find(params[:id])
#create new object with attributes of existing record
#post = Post.new(#existing_post.attributes)
render "your_post_form"
end

I found these answers a little hard to follow. One answer shows this:
#post = Post.new(#existing_post.attributes)
which will not work as it will also pass the id, and timestamp values. I used .dup to fix that and I show that in my answer.
Here's how I achieved creating a new item from an existing item.
The model is for a Product, the controller Products_Controller.rb. We're going to add a new action to the controller called copy and we're going to link to it from the show view on an existing Product and render a filled out new view ready to be edited and saved.
First we create a route for the copy action in routes.rb
# Routes.rb
resources :Products do
member do
get 'copy'
end
end
Then a copy action in Products_controller.rb
# ProductController.rb
def copy
#source = Product.find(params[:id])
#product = #source.dup
render 'new'
end
Now we need to add a Link to the show view to call our copy action.
# show.html.erb
<%= link_to "copy", copy_product_path(params[:id]) %>
Rails 4-6 Update:
The strong parameter scaffold makes it even shorter:
# ProductController.rb
# GET /products/1/copy
def copy
#product = #product.dup
render :new
end
And in the erb template:
# show.html.erb
<%= link_to "copy", copy_product_path(#product) %>

class Foo < ActiveRecord::Base
def self.clone_from(parent)
parent = find(parent) unless parent.kind_of? Foo
foo = self.new
foo.attributes = parent.attributes
# if you want to also clone a habtm:
foo.some_association_ids = parent.some_association_ids
# etc.
foo
end
end
class FoosController < ApplicationController
def clone
foo = Foo.clone_from(params[:id])
respond_with(foo)
end
end

Also worth mentioning is the dup method on a model. It makes a copy with all attributes and outgoing relations but sets id to nil. Like this (borrowing code from Naren Sisodiya):
def create_from_existing
#existing_post = Post.find(params[:id])
#create new object with attributes of existing record
#post = #existing_post.dup
render "your_post_form"
end

Related

rails 3 nested resource `new` path being switched

I don't quite get why this is happening, maybe someone of you does... here it goes.
I've created a nested resource:
resources :order do
resources :ordered_vehicles
end
I've added a link_to the new action and passed the order.id like so new_order_ordered_vehicle_path(order.id) the page is loaded nicely. The problem is after I press the button to submit the choice. He switches the path from http://localhost:3000/order/3/ordered_vehicles/new to http://localhost:3000/order/R076027535/ordered_vehicles and displays error Couldn't find Order with id=R076027535... go figure.
The error is being raised in the controller in this method
private
def find_order
#order = Order.find(params[:order_id])
end
Which is a before_filter.
the new.html.haml file looks like this
= form_for [#order, #ordered_vehicle], html: { multipart: true } do |f|
= #order.number
%br= #order.id
= f.fields_for :vehicles do |car|
.... #some fields here
= car.submit "Save your choice"
That id he can't find is the #order.number but I don't get why the switch.
EDIT:
Just to be thorough, I'll add the controller methods:
def new
#ordered_vehicle = #order.ordered_vehicles.build(params.slice(:order_id, :vehicle_id))
end
def create
binding.pry
#ordered_vehicle = #order.ordered_vehicles.build(params.slice(:order_id, :vehicle_id))
if #ordered_vehicle.save
flash[:notice] = "Save successful."
redirect_to account_path
end
end
POST request (I hope that's the one, still new to all this stuff):
"action_dispatch.request.formats"=>[text/html]},
#request_method="POST", #filtered_parameters={"utf8"=>"✓", "authenticity_token"=>
"Ar4vy8pqCSpA2ch0qG0qiJXAJUbNALYxm/FbuKbdzCc=", "ordered_vehicle"=>
{"vehicles"=> {"maker_id"=>"", "model_id"=>"", "year"=>"", "body"=>"", "capacity"=>"", "id"=>"1"}},
"commit"=>"Save your choice", "action"=>"create",
"controller"=>"spree/ordered_vehicles", "order_id"=>"R076027535"}, #method="POST",
#fullpath="/order/R076027535/ordered_vehicles">
As per request ;)
Well, it turned out to be a problem with Spree which I'm currently tweaking (I know I didn't mention it explicitly, but didn't want to just post too much information).
Bottom line:
In the Order model the method to_param was overwritten to pass the number column in to the params. Didn't overwrote it again, just left it there and adapted. In my find_order method I wrote:
def find_order
#order = Order.find_by_number(params[:order_id])
end
Also I've stored the order number in the table, there was a problem I believe with out that, but can't remember explicitly. Anyways, thanks for the help.
P.S. Sorry for the mess

How to customize the will paginate links with images

I want to customize the will paginate links in to dotted images instead of numbers 1,2,3. I tried to customize the css of pagination class but it wasn't worked. Pasted below the links format.
(Existing) Previous 1 2 3 Next
(Need to implement) . . . . . . .
I know this question is old, but perhaps you still want an answer. This is probably a good starting point. Create a helper such as app/helpers/will_paginate_helper.rb with the following content:
module WillPaginateHelper
class DottedLinkRenderer < WillPaginate::ActionView::LinkRenderer
protected
def link(text, target, attributes = {})
if target.is_a? Fixnum
attributes[:rel] = rel_value(target)
target = url(target)
end
attributes[:href] = target
tag(:a, ".", attributes)
end
end
def dotted_will_paginate(collection, options = {})
will_paginate(collection, options.merge(:renderer => WillPaginateHelper::DottedLinkRenderer))
end
end
then in your view use:
<%= dotted_will_paginate #posts %>
This is basically a custom link renderer based on the original link renderer.

How to get the current view name from layout template in Rails?

Is it possible to get the name of the currently rendered view from inside layout?
I did something like this for css namespacing:
# config/initializers/action_view.rb
ActionView::TemplateRenderer.class_eval do
def render_template_with_tracking(template, layout_name = nil, locals = {})
# with this gsub, we convert a file path /folder1/folder2/subfolder/filename.html.erb to subfolder-filename
#view.instance_variable_set(:#_rendered_template, template.inspect.gsub(/(\..*)/, '').split('/')[-2..-1].join('-'))
out = render_template_without_tracking(template, layout_name, locals)
#view.instance_variable_set(:#_rendered_template, nil)
return out
end
alias_method_chain :render_template, :tracking
end
# application.html.erb
<body class="<%= :#_rendered_template %>" >
Use <% __FILE__ %> to get the complete file path of current view, but you can only use it from within the file itself without writing some helpers
The method active_template_virtual_path method returns the template as a name in the following form "controller/action"
class ActionController::Base
attr_accessor :active_template
def active_template_virtual_path
self.active_template.virtual_path if self.active_template
end
end
class ActionView::TemplateRenderer
alias_method :_render_template_original, :render_template
def render_template(template, layout_name = nil, locals = {})
#view.controller.active_template = template if #view.controller
result = _render_template_original( template, layout_name, locals)
#view.controller.active_template = nil if #view.controller
return result
end
end
I had a similar question. I found <%= params[:controller] %> and <%= params[:action] %> to suit my need, which was to add the controller name and action name as classes on the body tag.
Just in case that helps anyone. :)
I'm currently using a modified version of Peter Ehrlich's solution. The resulting string is of the form controller_name/view_name, e.g. users/new, which means it can be passed directly to render later on, or altered to suit other uses. I've only tried this with Rails 4.2, though as far as I know it ought to work all the way back into the 3.xes.
ActionView::Base.class_eval do
attr_accessor :current_template
end
ActionView::TemplateRenderer.class_eval do
def render_template_with_current_template_accessor(template, layout_name = nil, locals = {})
#view.current_template = template.try(:virtual_path)
render_template_without_current_template_accessor(template, layout_name, locals)
end
alias_method_chain :render_template, :current_template_accessor
end
For debugging purpose, you can use gem 'current_template' from here.
This gem inspects logfile and display file name of view/partial template.
For example:
Also, you can simply add this line
<%= "#{`tail log/development.log`}".scan(/\s[a-z]+\/\S+/) %>
to your layout/application.html.erb.

Rails 3 Paths not working as expected

I have a form to create a new event for an organisation. The routing is:
resource :organisations do
resource :events
end
When I edit an event, on success I route back to the show action in the events controller as such:
def update
#organisation = current_user.organisations.find(params[:organisation_id])
#event = #organisation.events.find(params[:id])
if #event.update_attributes(params[:event])
# Handle a successful update.
flash[:success] = "Event updated"
redirect_to organisation_event_path
else
render 'edit'
end
end
When I create an event, I also want it to redirect to the events show action, implemented as follows:
def create
#organisation = current_user.organisations.find(params[:organisation_id])
#event = #organisation.events.create(params[:event])
if #event.save
flash[:success] = "Event added!"
redirect_to organisation_event_path
else
render 'new'
end
end
However, this produces the following error: No route matches {:action=>"show", :controller=>"events"}
This is because the URI does not have the event ID / name in it, as far as I can figure out. It is probably my lack of understanding of path generation, but how do I achieve the result I want?
I am not sure why your first redirect worked, but organisation_event_path will require two arguments since it is creating path from the ids of two resources - organization and event. So, you should call it like this:
organisation_event_path(#organization, #event)

Rails 3: Will_paginate's .paginate doesn't work

I'm using newes Rails 3 version with will_paginate.
#videos = user.youtube_videos.sort.paginate :page => page
I also added the ##per_page attribute to my youtube_video-model.
But it just won't paginate it. I get always all items in the collection listed.
What have I done wrong?
Yours, Joern.
Why are you calling sort here? That seems unnecessary, and probably would result in it finding all videos and calling pagination on that rather than paying any attention to any variable defined in your Video model. Instead, move the sorting logic into the Video model by using a scope or use the order method.
Here's my solution, my own answer, for all other's having trouble with will_paginate and reading this issue:
Create an ApplicationController method like this:
def paginate_collection(collection, page, per_page)
page_results = WillPaginate::Collection.create(page, per_page, collection.length) do |pager|
pager.replace(collection)
end
collection = collection[(page - 1) * per_page, per_page]
yield collection, page_results
end
Then in your Controller, where you got the collection that should be paginated:
page = setup_page(params[:page]) # see below
#messages = Message.inbox(account)
paginate_collection(#messages, page, Message.per_page) do |collection, page_results|
#messages = collection
#page_results = page_results
end
And in your View:
<% #messages.each do |message| %>
<%# iterate and show message titles or whatever %>
<% end %>
<%= will_paginate #page_results %>
To get the page variable defined, check this:
def setup_page(page)
if !page.nil?
page.to_i
else
1
end
end
So page = setup_page(params[:page]) does the trick, with that simple method.
This WORKS!