ActiveAdmin Generic Form Partial - ruby

I have the following form for uploading PDFs:
f.inputs 'PDF' do
f.has_many :pdfs, allow_destroy: true do |pdf_f|
pdf_f.semantic_errors
pdf_f.input :name
pdf_f.input :description
pdf_f.input :enabled
pdf_f.input :link, label: 'file', as: :file
end
end
And a way to display the file:
li do
div "name: #{pdf.name}"
div "filename: #{pdf.link.path.split('/').last}"
div raw "description: <br> #{pdf.description}"
div "enabled: #{pdf.enabled ? "yes" : "no"}"
end
Pdf is a polymorphic object thus can appear on several models.
How do I create a generic shard partial for each of them that can then be re-used across ActiveAdmin so I don't have to recreate them every time?
Bear in mind the second one I expect to iterate over it by passing it in as a collection:

For the form answer is to place the form in a partial that resides in:
app/views/active_admin/base/_pdf_form.erb
and then render it like this:
render partial: 'pdf_form', locals: {f: f}
For the display parital you should place the partial at:
app/views/active_admin/base/_pdf.erb
and then render it like this:
render partial: 'pdf', collection: event.pdfs
The partial name needs to be in quotes otherwise rails won't look for it active_admin/base.

Related

How do you establish the parent of an object as an object in page object?

In a page object file:
class ThisPage
include PageObject
I can establish an object like this:
div(:user_dialog, :class => 'ud_dialog')
However, in the domain of the website, there are many windows with :class => 'ud_dialog' that popup in various workflows.
I can get to the object in binding.pry like this:
on(ThisPage).div_elements(:text => 'Are you sure you want to do this action?').first.parent.html
How can I establish the window like this in the page file?
i.e. is there some syntax like this:
div(:user_dialog, parent(:text => 'Are you sure you want to do this action?'))
For complex locators, such as locating the parent element, you can use a block to get the element.
If you define the div in your page object as:
div(:user_dialog){
div_elements(:text => 'Are you sure you want to do this action?').first.parent
}
Then your page can do:
on(ThisPage).user_dialog_element.html
Note that since you want the first matching div, you could simplify it to:
div(:user_dialog){
div_element(:text => 'Are you sure you want to do this action?').parent
}
It might also be possible to be more direct by using multiple locators (depending on what your html is). You could locate the div that has the ud_dialog class and contains the specified text (a partial match since there is likely other text):
div(:user_dialog, :class => 'ud_dialog',
:text => /Are you sure you want to do this action?/)

How to generate pages for each tag in nanoc

I am new to nanoc and I am still finding my around it. I am able to get my site ready, it looks good and functions good, too. But I need to have a tags area. I am able to achieve that with
<%= tags_for(post, params = {:base_url => "http://example.com/tag/"}) %>
But how do I generate pages for tag? So for instance there is a tag called "NFL", so every time a user clicks on it, he/she should be directed to http://example.com/tag/nfl with a list of articles that correspond with NFL.
I can setup a layout which will do that. But then what kind of logic should be I using? And also do I need to have a helper for this?
You can use a preprocess block in your Rules file in order to generate new items dynamically. Here’s an example of a preprocess block where a single new item is added:
preprocess do
items << Nanoc::Item.new(
"some content here",
{ :attributes => 'here', :awesomeness => 5000 },
"/identifier/of/this/item")
end
If you want pages for each tag, you need to collect all tags first. I’m doing this with a set because I do not want duplicates:
require 'set'
tags = Set.new
items.each do |item|
item[:tags].each { |t| tags.add(t.downcase) }
end
Lastly, loop over all tags and generate items for them:
tags.each do |tag|
items << Nanoc::Item.new(
"",
{ :tag => tag },
"/tags/#{tag}/")
end
Now, you can create a specific compilation rule for /tags/*/, so that it is rendered using a "tags" layout, which will take the value of the :tag attribute, find all items with this tag and show them in a list. That layout will look somewhat like this:
<h1><%= #item[:tag] %></h1>
<ul>
<% items_with_tag(#item[:tag]).each do |i| %>
<li><%= link_to i[:title], i %></li>
<% end %>
</ul>
And that, in broad strokes, should be what you want!

Trying to populate gmaps4rails with multiple json strings in one page

I hope I am asking this right, so please let me know if I'm way off.
The problem is trying to build a homepage that draws from multiple controllers, to display the nearest locations from multiple controllers, ie. food, businesses, ect.
Right now the individual listings pages have maps drawn from their respective
#json = Controller.all.to_gmaps4rails
How would I do something like :
#json = Controller1 Controller2 .all.to_gmaps4rails
I hope this isnt a noob question and I'm just having a bad day. Thanks guys!
edit 12.5.2011 #seanhill - this is one of the models, the other sections are very close to this format. First off, I wasn't even sure if my homepage requires it's own model, as it doesn't interact with the db at all, more pulling data from controllers that do the work. Thanks for the response Sean!
class Dining < ActiveRecord::Base
validates_uniqueness_of :name, :message => "already exists"
attr_accessible :name, :address, :cuisine, :latitude, :longitude, :about, :facebook, :twitter, :phone, :website
geocoded_by :address
after_validation :geocode, :if => :address_changed?
acts_as_gmappable :process_geocoding => false
def gmaps4rails_address
"#{self.address}"
end
def gmaps4rails_infowindow
"<h3>#{self.name}</h3><br /><h5>#{self.cuisine}</h5>"
end
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
scoped
end
end
end
Try this
holder = Controller1.all
holder << Controller2.all
#json = holder.flatten.map{|h| {lng: h.longitude, lat: h.latitude, class: h.class.to_s}}.to_json
Make sure to change longitude and latitude based on your column names and use js to manipulate the markers based upon class.
As the #Sean Hill said you shouldn't be calling .all on controllers but I think you have a slight misunderstanding of how things are working. Assuming you have a Model called Dining and another called Shop, when you call Dining.all or Shop.all inside class DiningsController < ApplicationController, you are calling .all on either the Dining Model or the Shop Model not on the DiningsController.
The information you display through a controller is only limited by the methods you call in it although it is best practice ensure the main focus of the information displayed is related to the respective controller.
So what you are really trying to do is get the records from multiple models and group them together to display them in a single map.
With that said the answer should read something like this
holder = Dining.all # Takes all Dining records returned as an array and sets them to holder variable
holder << Shop.all # Pushes the Shop records array into the holder with the dining records
holder.flatten!# Next we flatten the array so we only have a single array.
# Then we use the map method to run the given code one time for each instance
# in the holder array to extract the info we need. The results for every instance
# in holder are returned in an array which we then convert to_json.
#json = holder.map{|h| {lng: h.longitude, lat: h.latitude, class: h.class.to_s}}.to_json
#json1 = something.to_gmaps4rails
#json2 = something.to_gmaps4rails
#json = (JSON.parse(#json1) + JSON.parse(#json2)).to_json
I populated the map with my initial data of festivals, and then added the rides to it with javascript with this code,
<% content_for :scripts do %>
<script type="text/javascript">
Gmaps.map.callback = function() {
$.getJSON('/rides_gmap', function(data){
Gmaps.map.addMarkers(data);
});
}
</script>
<%end%>
In the rides controller I had this
def rides_gmap
#rides = Ride.all
#json = #rides.to_gmaps4rails do |ride, marker|
marker.infowindow render_to_string(:partial => "/rides/infowindow", :locals => { :ride => ride})
marker.picture({
'picture' => view_context.image_path("orange-dot.png"),
'width' => 20,
'height' => 20
})
marker.title "#{ride.address}"
marker.json({:ride_id => ride.id, :ride_festivaltype => ride.festival.festivaltype
end
respond_with #json
end
I hope this helps.

How do I get an array of check boxes in haml?

I have an array of strings, called #theModels, in a routine implemented as part of a Sinatra server. These models are options for the user to select, and are obtained by the back end (the idea being, as new models are added, then the front end code should not change).
I'm using haml to render html.
How can I enumerate each element in the list of #theModels such that each element is a checkbox? And how can I obtain which checkboxes the user has selected?
I see that just putting
= #theModels
will give me the list of strings contained in #theModels, but without spacing or the like, and certainly not in checkboxes. I've found this question that appears to be similar, but my haml-fu isn't good enough to convert that into what I need.
UPDATE:
These are options associated with a file upload, such that now the code looks like:
%form{:action=>"/Upload",:method=>"post",:enctype=>"multipart/form-data"}
- #theModelHash.each do |key,value|
%br
%input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
=key
%input{:type=>"file",:name=>"file"}
%input{:type=>"submit",:value=>"Upload"}
Problem is, that puts a file upload button on each option, instead of at the end. I only want one submit button in the end; should I have two forms that both report their results when the 'Upload' button is pressed?
UPDATE2:
After a moment's thought, the above can be modified to:
Thanks!
%form{:action=>"/Upload",:method=>"post",:enctype=>"multipart/form-data"}
- #theModelHash.each do |key,value|
%br
%input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
=key
%form{:action=>"/Upload",:method=>"post",:enctype=>"multipart/form-data"}
%input{:type=>"file",:name=>"file"}
%input{:type=>"submit",:value=>"Upload"}
And that appears to do what I want.
I think you should send the content as an hash instead.
This will give you the opportunity to set initial values in the form.
The hash #params will give you the result.
E.g. {"oranges"=>"1"}
#app.haml
%form{:method => 'post', :action => "/"}
- #models.each do |key,value|
%br
%input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
=key
%input{:type => :submit, :value => "Save"}
#app.rb
require 'sinatra'
require 'haml'
get '/' do
#models = {"oranges" => true, "bananas" => false}
haml :app
end
post '/' do
#params.inspect
end
The link you provided linked to a rails solution where you have a function returning the proper html.
You can define this function yourself:
Input: key, value
Output: %input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
def check_box(key, value)
...
end
and call it in haml with
=check_box(key,value)

Add readable field descriptions to ActiveRecord models

I'd like to add descriptions to ActiveRecord model fields to serve as basic instructions / examples for each of the fields. Basically model metadata. I can then display these in the UI (next to the fields on a form etc.)
The way I'm planning to do it is simply create a static hashtable inside the model with the field name as the key and description as the value. I.e.
FIELD_DESCRIPTIONS = {
'category' => 'Select the category it should appear within.',
'title' => 'The title should be a short but descriptive summary.',
'description' => 'Please enter a full description.'
}
etc.
Then I would create a a basic form helper that would wrap these explanations inside of a span (initially hidden and shown via jQuery) so they could be instatiated via f.field_description(:title) or something along those lines.
Anyone have any better ideas? I'd like to keep this field metadata in the model since many views could use the same information, and I also think it's nice to have descriptions within the model when you're going back to look at the code (like how DataMapper can be used right within the model to specify fields).
To give you a little more detail on what I've already done (and it works fine) here's the code. I think there has to be a prettier way of expressing these descriptions in the model, so let me know if you have any ideas.
In model:
FIELD_DESCRIPTIONS = {
'category' => 'Select the category it should appear within.',
'title' => 'The title should be a short but descriptive summary.',
'description' => 'Please enter a full description.'
}
def self.describe_field(field)
FIELD_DESCRIPTIONS[field]
end
In application_helper.rb
def field_helper(form, field)
"<span class='field_helper'>#{form.object.class.describe_field(field)}</span>"
end
In view:
<%= field_helper(f, 'title') %>
This will produce the desired output:
<span class='field_helper'>The title should be a short but descriptive summary.</span>
UPDATE:
Ok So this is the final code I'm using based on the accepted answer.
File: /config/initializers/describe_attr.rb
if defined?(ActiveRecord)
# let's us add attribute descriptions to each AR model
class ActiveRecord::Base
def self.describe_attr(*params)
attrs = params.shift
unless attrs.nil?
case attrs
when Hash
##attr_descriptions = attrs
when Symbol
return ##attr_descriptions[attrs]
end
end
##attr_descriptions ||= {}
end
end
end
File: /app/models/project.rb
describe_attr(
:category => 'Select the category the project should appear within.',
:title => 'The title should be a short but descriptive summary of the project.',
:description => 'Describe the project in detail.',
:image => 'Upload an image for the project.'
)
File: /app/helpers/application_helper.rb
# assumes you have a style defined for attr_description
def describe_attr(form, attribute)
"<span class='attr_description'>#{form.object.class.describe_attr(attribute)}</span>"
end
File: /app/views/projects/_form.html.erb
<%= describe_attr(f, :title) %>
The hash is a reasonable simple solution, but if you're on rails 2.2 or higher you might want to try the internationalization api to do this. This would also put you in a good place if you ever wanted to add translations.
Check out the i18n guide for details, but basically you would create a config/locales/en.yml that includes your column names like:
en:
labels:
category: Select the category it should appear within.
Then in your view:
<%= t('labels.category') %>
The namespace is your call of course. Also check out section 4.1.4 for a neat way to separate translations based on your current view template.
If you want to patch ActiveRecord, then you can do something like:
# Add this at the bottom of enviroment.rb
class ActiveRecord::Base
def self.field_description(*params)
attrs = params.shift
unless attrs.nil?
case attrs
when Hash
##field_description = attrs
when Symbol
return ##field_description[attrs]
end
end
##field_description ||= {}
end
end
And inside your model you can add this line like a macro:
class Product < ActiveRecord::Base
field_description :category => 'Select the category it should appear within.',:title => 'The title should be a short but descriptive summary.',:description => 'Please enter a full description.'
end
To get the value
Product.field_description : title
You can mix your solution with the label helper itself:
f.label :title, f.describe(:title)
And in your model:
FIELD_DESCRIPTIONS = {
:category => 'Select the category it should appear within.',
:title => 'The title should be a short but descriptive summary.',
:description => 'Please enter a full description.'
}
def describe(:field)
self.class::FIELD_DESCRIPTIONS[field]
end
Be sure to check out formtastic, which includes support for internationalized (or not) field labels as per matschaffer's answer.
http://github.com/justinfrench/formtastic/

Resources