ActiveAdmin and handling a subset of data - ruby-on-rails

We use ActiveAdmin and have a notion of an event at like http://domain.com/admin/events/3/edit. I'd like to be able to edit the finance data aspect of this (something like http://domain.com/admin/events/3/finance/edit or http://domain.com/admin/events/3/edit/finance). How would you organize it? I was thinking of trying to do a custom controller like this:
ActiveAdmin.register Event, as: 'Finance' do
permit_params %i(venue_id name event_type_id)
before_create do |event|
event.created_by = current_user
end
show do |event|
panel 'Details' do
attributes_table_for event do
row :id
end
end
end
end
but this doesn't seem to work. What would be the most basic pattern to have a controller which has a portion of our data and can work well within ActiveAdmin?

You appear to be asking about nesting a resource inside another, see 'Belongs To' on this page

Related

How do i define multiple paginators for the same page on active admin?

I am trying to create a simple dashboard on active admin.
The idea here is to have multiple panels that serve as a quick view of different models. The problem is that when I move between pages it affects all paginators.
I am doing this
paginated_collection(my_collection.page(params[:page_2]).per(5)) do
table_for collection do |t|
...
end
end
Is it possible to have multiple paginators on active admin for the same page?
It is possible to set the name of the page param. This question is closely related to this answer: https://stackoverflow.com/a/6721703/790737 . ActiveAdmin wraps Kaminari, but will pass the param_name parameter to the pagination links. So I think you should be able to write:
paginated_collection(my_collection.page(params["custom_page_param"]).per(5), :param_name => 'custom_page_param') do
table_for collection do |t|
...
end
end
Good luck!

Rails3 Admin UI frameworks

We will soon be rewriting a 5 year old rails app, with a very unsound code foundation, from scratch in a brand new Rails 3 app with all the new hotness. The current app has a substantial custom admin UI backend which depends on now admin frameworks at all. Just some base controller classes and some somewhat useful CSS conventions. But maintaining that UI is a lot of work, especially if we want it to look half way nice.
So I'm in the market for an Admin UI framework that will make the simple stuff trivial, but without getting the way of more complex customization in both form and function.
The top contender, ActiveAdmin, seems to be very popular, and after playing with it a bit I have some concerns. It seems to declare a whole unique DSL that exists in a single ruby file. This is kind of neat, but it's also a completely different than how most other Rails apps are architected. It abstracts away the view, the helper the controller, and gives you a pure ruby DSL. It seems to me that this would get in the way of doing tricky things, more advanced custom things in our admin views. DSL's are great, until you want to do something they don't explicitly support.
Example "Resource" from my experimentation, with no controller and no view.
ActiveAdmin.register Region do
menu parent: "Wines"
show title: :name
index do
column(:zone) { |o| link_to o.zone, admin_region_path(o) }
column(:name) { |o| link_to o.name, admin_region_path(o) }
default_actions
end
end
So, the questions:
Is not being based on the standard Rails MVC architecture in separate files, and the typical controller inheritance based implementation of an admin area, actually something I should be concerned about? Will it hamper extensibility in the long term?
Is the DSL in ActiveAdmin better and more flexible than I'm giving it credit for?
Should I be looking at some other framework that lends itself better to my goals of high customization?
Should I stop being lazy and roll my own?
Does the choice of Mongoid instead of MySQL as a database affect any of the above questions?
It is worth mentioning that in ActiveAdmin you can also create your own complex forms by using partials
form partial: 'my_form'
and expand controller functions with a controller block.
controller do
def edit
end
def index
end
end
+1 for active admin, I use it in many projects, including a cms I'm building. It indeed is more flexible than many people who are newer with it give it credit for, at the end of the day you can always do:
controller do
def custom_action
Puts "hi"
end
end
(Think that's the right syntax writing from phone so all this is off top of head)
Also, I swear by inherited resources, which active admin controllers extend, as they really force you (in a good way) into writing restful, reuse able code. Bottom line is I believe active admin is leaps and bounds ahead of the others I've tried (railsadmin and at least one other)
Update:
Sure, here is the inherited_resources documentation
https://github.com/josevalim/inherited_resources
And here is an example of modifying the controller directly, from my little CMS project.
ActiveAdmin.register Channel do
index do
column :title
column :slug
column "Fields" do |channel|
"#{link_to('View Fields', admin_channel_entry_fields_path(channel))} #{link_to 'Add Field', new_admin_channel_entry_field_path(channel)}".html_safe
end
column "Actions" do |channel|
"#{link_to('View Entries', admin_channel_entries_path(channel))} #{link_to 'Create Entry', new_admin_channel_entry_path(channel)}".html_safe
end
default_actions
end
form :html => { :enctype => "multipart/form-data" } do |f|
f.inputs "Channel" do
f.input :title
f.input :display_name
f.input :image
end
f.buttons
end
controller do
def begin_of_association_chain
current_site
end
def tags
query = params[:q]
if query[-1,1] == " "
query = query.gsub(" ", "")
ActsAsTaggableOn::Tag.find_or_create_by_name(query)
end
#tags = ActsAsTaggableOn::Tag.all
#tags = #tags.select { |v| v.name =~ /#{query}/i }
respond_to do |format|
format.json { render :json => #tags.collect{|t| {:id => t.name, :name => t.name }}}
end
end
end
end
Basically, I am using the inherited resources, begin_of_association_chain method (one of my favorite things about IR), to scope all the data within channels, or any of the admin resources that inherit from my channels resource, to the current site, without having a url like /admin/sites/1/channels -- Because I am already setting current_site based on the url the visitor enters with. -- Anyways, basically once you are inside:
controller do
puts self.inspect
end
Returns the actual controller itself, e.g. Admin::ChannelsController (which < InheritedResources::Base, maybe not directly but all the IH controller methods should be available at this point).

ActiveAdmin display default view content

I am working with ActiveAdmin and need to make customizations to some views and have come across a couple of scenarios I feel I am doing wrong.
I am adding an additional table to a show view (comments on Posts). This requires me to rewrite the whole attributes table and then add my panel. Is there a way to customize views without losing the default content?
I would also like to add a table of associated items on the show view which doesn't need to be customized is there any way to include the default tale that would normally be on the index view with default actions and paging?
After digging in the source code of Active Admin, I've found a way to patch this
show do
default_main_content
panel "Your Added Stuff" do
# Add stuff here
end
end
Of course this is undocumented and maybe considered a hack, but unless any other solution exists, it works.
Note: To do this in the form action (new and edit):
form do |f|
f.inputs
# Other inputs here
f.actions
end
Instead of using default_main_content, you could also just loop through the columns on the model like so:
ActiveAdmin.register Ad do
show do
attributes_table do
default_attribute_table_rows.each do |field|
row field
end
# Custom bits here
end
end
end
A couple areas of the documentation might help you:
See Customize the Show Page, Customizing the Index Page, Customizing the Form, and Custom Pages. An example of customizing a show screen:
ActiveAdmin.register Ad do
show do |ad|
default_main_content
h3 ad.title
end
end
See Custom Action Items in the Custom Controller Actions section of the documentation. An example:
action_item :only => :show, :if => proc{ current_admin_user.super_admin? } do
"Only display this to super admins on the show screen"
end
NB default_main_content does not exist in the documentation anymore, yet it works fine.
Just figured that out myself:
For the default table index page you can do something like this
index do
h1 "Hello World"
p "get more content"
instance_eval(&default_table)
end

Rails: Create a custom UI for a single specific object

I've run into a problem I'm completely unsure how to approach.
I have an app for sharing architectural photos. Users have_many Photos, and users can create Collections which also have_many Photos.
Now I have one customer who is a big name in the industry who would like to work with me to create a totally customized Collection with a very different look and feel from "regular" collections, but essentially the same functionality underneath. I'd like to accommodate this request, but I really have no idea how to do it.
Given that I already have a functioning Collection model and CollectionsController, plus all the views, I'd like to re-use as much of that as possible. So, for instance, the custom Collection needs to override the user facing :show view, but not the admin :edit view.
How would you approach something like this?
I'm trying to understand the most efficient, DRY method for creating a completely custom UI for a single record in the database. I'd be very appreciative of suggestions, including links to articles / books etc, as I haven't been able to find much in this area.
I would allow the creation of Liquid view templates associated with a User and/or Collection (if you want both - per-user templates with per-collection variations - use a polymorphic association) and of course fall back to your default view (also built with Liquid for consistency and reference) for all cases where no custom template is found.
Edit to add suggested details:
Any custom templates should be stored in the database (I would add a test/preview function so the user entering a custom template has the chance to verify their template before publishing it):
# Table name custom_templates
# id :integer
# templatable_type :string
# templatable_id :integer
# contents :text
class CustomTemplate < ActiveRecord::Base
belongs_to :templatable, :polymorphic => true
end
class User
has_one :custom_template, :as => :templatable
end
class Collection
has_one :custom_template, :as => :templatable
end
In your controller action, look for a custom template:
custom_template = #collection.custom_template
custom_template ||= #user.custom_template
#custom_template = Liquid::Template.parse(custom_template.contents) if custom_template
In your view, either render the custom template or your default template partial:
<% if #custom_template -%>
<%= #custom_template.render(_hash_of_objects_to_pass_to_liquid_template_) %>
<% else -%>
<%= render :partial => 'default' %>
<% end -%>

DRYing up Rails Views with Nested Resources

What is your solution to the problem if you have a model that is both not-nested and nested, such as products:
a "Product" can belong_to say an "Event", and a Product can also just be independent.
This means I can have routes like this:
map.resources :products # /products
map.resources :events do |event|
event.resources :products # /events/1/products
end
How do you handle that in your views properly?
Note: this is for an admin panel. I want to be able to have a "Create Event" page, with a side panel for creating tickets (Product), forms, and checking who's rsvp'd. So you'd click on the "Event Tickets" side panel button, and it'd take you to /events/my-new-event/tickets. But there's also a root "Products" tab for the admin panel, which could list tickets and other random products. The 'tickets' and 'products' views look 90% the same, but the tickets will have some info about the event it belongs to.
It seems like I'd have to have views like this:
products/index.haml
products/show.haml
events/products/index.haml
events/products/show.haml
But that doesn't seem DRY. Or I could have conditionals checking to see if the product had an Event (#product.event.nil?), but then the views would be hard to understand.
How do you deal with these situations?
Thanks so much.
I recommend you to make separate admin controller with it's own views to administrate everything you want. And your customer's logic stayed in products contoller.
I don't have good and clean solution for this problem. Usualy if views doesn't differ to much, I use single view and add some code like #product.event.nil?. You can always add some variable, or helper that will make this method shorter, on example has_event? - then your view will look cleaner. And use it in code like this:
<% if has_event? %>
some html
<% end %>
or for single line:
<%= link_to 'Something special', foo_path if has_event? %>
On the other side, you can create few partials that are the same for both views, put them in some folder, on example /shared/products/... and render them from your views like this:
<%= render :partial => '/shared/products/info' %>
and so on.
But if they don't differ too much, I really would use if version.
The views will be handled by the ProductsController. You can alter the logic in your controller depending on the nesting of the resource.
# app/controller/products_controller.rb
# ...some code...
def index
#event = Event.find_by_id(params[:event_id]) if params[:event_id]
#products = #event ? #event.products : Product.all
end
The view will be handled by the usual product view
# app/views/products/index.html.haml
- unless #products.blank?
- #products.each do |product|
%p= product.some_attribute

Resources