Is is possible to render a normal template html.erb template with active admin? - ruby-on-rails-3

I have a custom page in my admin built with active admin:
In app/admin/stats.rb:
ActiveAdmin.register_page 'Stats' do
controller do
def index
#foo = 'bar'
end
end
end
And in app/views/admin/stats/index.html.erb:
<h1> Admin stats</h1>
<p><%= #foo %></p>
When I go to /admin/stats, I see my page, but without the normal admin layout (like on the dashboard page)
How can I decorate my page with the default layout ?
I've tried:
ActiveAdmin.register_page 'Stats' do
content do
'foobar'
end
controller do
def index
#foo = 'bar'
end
end
end
But this doesn't change anything. Still my stat page with no layout. Any ideas ?

This is one way to do it:
In app/admin/stats.rb:
ActiveAdmin.register_page 'Stats' do
content do
render 'index'
end
controller do
def index
#foo = 'bar'
end
end
end
And rename app/views/admin/stats/index.html.erb to app/views/admin/stats/_index.html.erb (Notice the _)
And it works fine.
From what I understand, if index.html.erb is present in views/admin/stats, the content block is not called. If index.html.erb is renamed into something else, then we go into the content block, then the layout rendering is called...

Related

how to append/replace alt/title attribute in all image tags?

say I have an action template like this
# home/index.html.erb
<%= img_tag "logo.gif" %>
if I want to add alt/title attribute to it, I can just do
# home/index.html.erb
<%= img_tag "logo.gif", alt: "alt!!", title: "title!!" %>
but I have 1000 image tags and I don't want to change every each one of them.
I then thought of using rack middleware and modify image tags before outputting from server.
http://railscasts.com/episodes/151-rack-middleware?view=asciicast
doc = Nokogiri.HTML(#response.body)
doc.search("img").each do |tag|
[:alt, :title].each{|attribute| tag[attribute] = "changed!!" }
end
but when I follow the railscast episode, it appends entire body on the top of the original rather than replacing it.
Am I doing it wrong in the rack, or is there a smarter way to do this?
Updated answer:
# /config/initializers/image_tag_helper.rb
module ActionView
module Helpers
module AssetTagHelper
def image_tag(source, options={})
options[:src] = path_to_image(source)
options[:alt] = "Default Alt" unless options.has_key?(:alt)
options[:title] = "Default Title" unless options.has_key?(:title)
tag(:img, options)
end
end
end
end
This overrides the image_tag helper method to set default alt and title attributes.

rspec rendered view fails to include variable data

I have a failing rspec view test but the code works - I probably have a variable incorrectly setup but can't figure out what it is.
When I display the contents of #incident_report (pp #incident_report) in my spec, it properly displays the record created by FactoryGirl.
When I display the actual rendered content (puts rendered), it shows the values from the the record I created with FactoryGirl...
But the "rendered.should contain(work_order)" spec fails with:
1) incident_reports/show.html displays the work order number on the incident
Failure/Error: rendered.should contain(work_order)
expected the following element's content to include "54785":
and none of the data is displayed, only the HTML template
spec/views/incident_report/show.html.haml_spec.rb code
require 'spec_helper'
describe "incident_reports/show.html" do
before(:each) do
#incident_report = Factory(:incident_report)
end
it "displays the work order number on the incident" do
work_order = #incident_report.work_order
pp #incident_report #displays an incident_report, id => 1
assign(:incident_report, #incident_report)
render
puts rendered #this DOES have the content from #incident_report
rendered.should contain("Work Order:")
rendered.should contain(work_order)
end
end
show.html.haml code
%h1 Display Incident Report
.navigation
= link_to 'Edit', edit_incident_report_path(#incident_report)
|
\#{link_to 'Back', incident_reports_path}
= render 'form'
.navigation
= link_to 'Edit', edit_incident_report_path(#incident_report)
|
\#{link_to 'Back', incident_reports_path}
Gotta be something really simple I'm overlooking.
Turns out it's because I was using simple_form and when simple_form displays for a "show" action, it puts the field values into the html as a 'value="54785"' attribute. If you display it in a browser, the labels and values all show up correctly, but rspec can't see them.
I had to add
rendered.should have_tag 'input', :with => { :value => "54765", :name => 'incident_report[work_order]' }
to my example to get it to work.
Seems like there should be a better solution but at least now I can continue testing.

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: 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!

Rails clone copy or duplicate

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