activeadmin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activeadmin might be problematic. Click here for more details.

Files changed (89) hide show
  1. data/.document +5 -0
  2. data/.gitignore +25 -0
  3. data/Gemfile +16 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +201 -0
  6. data/Rakefile +71 -0
  7. data/active_admin.gemspec +22 -0
  8. data/lib/active_admin.rb +229 -0
  9. data/lib/active_admin/action_builder.rb +60 -0
  10. data/lib/active_admin/action_items.rb +48 -0
  11. data/lib/active_admin/asset_registration.rb +34 -0
  12. data/lib/active_admin/breadcrumbs.rb +26 -0
  13. data/lib/active_admin/dashboards.rb +50 -0
  14. data/lib/active_admin/dashboards/dashboard_controller.rb +40 -0
  15. data/lib/active_admin/dashboards/renderer.rb +45 -0
  16. data/lib/active_admin/dashboards/section.rb +43 -0
  17. data/lib/active_admin/dashboards/section_renderer.rb +28 -0
  18. data/lib/active_admin/filters.rb +189 -0
  19. data/lib/active_admin/form_builder.rb +91 -0
  20. data/lib/active_admin/helpers/optional_display.rb +34 -0
  21. data/lib/active_admin/menu.rb +42 -0
  22. data/lib/active_admin/menu_item.rb +67 -0
  23. data/lib/active_admin/namespace.rb +111 -0
  24. data/lib/active_admin/page_config.rb +15 -0
  25. data/lib/active_admin/pages.rb +11 -0
  26. data/lib/active_admin/pages/base.rb +92 -0
  27. data/lib/active_admin/pages/edit.rb +21 -0
  28. data/lib/active_admin/pages/index.rb +58 -0
  29. data/lib/active_admin/pages/index/blog.rb +65 -0
  30. data/lib/active_admin/pages/index/table.rb +48 -0
  31. data/lib/active_admin/pages/index/thumbnails.rb +40 -0
  32. data/lib/active_admin/pages/new.rb +21 -0
  33. data/lib/active_admin/pages/show.rb +54 -0
  34. data/lib/active_admin/renderer.rb +72 -0
  35. data/lib/active_admin/resource.rb +96 -0
  36. data/lib/active_admin/resource_controller.rb +325 -0
  37. data/lib/active_admin/sidebar.rb +78 -0
  38. data/lib/active_admin/table_builder.rb +162 -0
  39. data/lib/active_admin/tabs_renderer.rb +39 -0
  40. data/lib/active_admin/version.rb +3 -0
  41. data/lib/active_admin/view_helpers.rb +106 -0
  42. data/lib/active_admin/views/active_admin_dashboard/index.html.erb +1 -0
  43. data/lib/active_admin/views/active_admin_default/edit.html.erb +1 -0
  44. data/lib/active_admin/views/active_admin_default/index.csv.erb +2 -0
  45. data/lib/active_admin/views/active_admin_default/index.html.erb +1 -0
  46. data/lib/active_admin/views/active_admin_default/new.html.erb +1 -0
  47. data/lib/active_admin/views/active_admin_default/show.html.erb +1 -0
  48. data/lib/active_admin/views/layouts/active_admin.html.erb +40 -0
  49. data/lib/activeadmin.rb +1 -0
  50. data/lib/generators/active_admin/install/install_generator.rb +31 -0
  51. data/lib/generators/active_admin/install/templates/active_admin.css +325 -0
  52. data/lib/generators/active_admin/install/templates/active_admin.js +10 -0
  53. data/lib/generators/active_admin/install/templates/active_admin.rb +47 -0
  54. data/lib/generators/active_admin/install/templates/active_admin_vendor.js +382 -0
  55. data/lib/generators/active_admin/install/templates/dashboards.rb +36 -0
  56. data/lib/generators/active_admin/install/templates/images/orderable.gif +0 -0
  57. data/lib/generators/active_admin/resource/resource_generator.rb +16 -0
  58. data/lib/generators/active_admin/resource/templates/admin.rb +3 -0
  59. data/spec/integration/dashboard_spec.rb +44 -0
  60. data/spec/integration/index_as_blog_spec.rb +65 -0
  61. data/spec/integration/index_as_csv_spec.rb +40 -0
  62. data/spec/integration/index_as_table_spec.rb +160 -0
  63. data/spec/integration/index_as_thumbnails_spec.rb +43 -0
  64. data/spec/integration/layout_spec.rb +82 -0
  65. data/spec/integration/new_view_spec.rb +52 -0
  66. data/spec/integration/show_view_spec.rb +91 -0
  67. data/spec/spec_helper.rb +104 -0
  68. data/spec/support/rails_template.rb +19 -0
  69. data/spec/unit/action_builder_spec.rb +76 -0
  70. data/spec/unit/action_items_spec.rb +41 -0
  71. data/spec/unit/active_admin_spec.rb +52 -0
  72. data/spec/unit/asset_registration_spec.rb +37 -0
  73. data/spec/unit/controller_filters_spec.rb +26 -0
  74. data/spec/unit/dashboard_section_spec.rb +63 -0
  75. data/spec/unit/dashboards_spec.rb +59 -0
  76. data/spec/unit/filter_form_builder_spec.rb +157 -0
  77. data/spec/unit/form_builder_spec.rb +238 -0
  78. data/spec/unit/menu_item_spec.rb +137 -0
  79. data/spec/unit/menu_spec.rb +53 -0
  80. data/spec/unit/namespace_spec.rb +107 -0
  81. data/spec/unit/registration_spec.rb +46 -0
  82. data/spec/unit/renderer_spec.rb +100 -0
  83. data/spec/unit/resource_controller_spec.rb +48 -0
  84. data/spec/unit/resource_spec.rb +197 -0
  85. data/spec/unit/routing_spec.rb +12 -0
  86. data/spec/unit/sidebar_spec.rb +96 -0
  87. data/spec/unit/table_builder_spec.rb +162 -0
  88. data/spec/unit/tabs_renderer_spec.rb +34 -0
  89. metadata +247 -0
@@ -0,0 +1,325 @@
1
+ require 'inherited_views'
2
+ require 'active_admin/pages'
3
+
4
+ module ActiveAdmin
5
+ class ResourceController < ::InheritedViews::Base
6
+
7
+ # Add our views to the view path
8
+ ActionController::Base.append_view_path File.expand_path('../views', __FILE__)
9
+ self.default_views = 'active_admin_default'
10
+
11
+ helper ::ActiveAdmin::ViewHelpers
12
+
13
+ layout 'active_admin'
14
+
15
+ class_inheritable_accessor :form_config
16
+
17
+ include ActiveAdmin::Breadcrumbs
18
+ include ActiveAdmin::Sidebar
19
+ include ActiveAdmin::ActionItems
20
+ include ActiveAdmin::Filters
21
+ include ActiveAdmin::ActionBuilder
22
+
23
+ respond_to :html, :xml, :json
24
+ respond_to :csv, :only => :index
25
+
26
+ add_breadcrumb "Dashboard", "/admin"
27
+
28
+ before_filter :add_section_breadcrumb
29
+ before_filter :set_current_tab
30
+ before_filter :setup_pagination_for_csv
31
+
32
+ class << self
33
+ # Reference to the Resource object which initialized
34
+ # this controller
35
+ attr_accessor :active_admin_config
36
+
37
+ def active_admin_config=(config)
38
+ @active_admin_config = config
39
+ defaults :resource_class => config.resource
40
+ end
41
+
42
+ def set_page_config(page, config)
43
+ active_admin_config.page_configs[page] = config
44
+ end
45
+
46
+ def get_page_config(page)
47
+ active_admin_config.page_configs[page]
48
+ end
49
+
50
+ def reset_page_config!(page)
51
+ active_admin_config.page_configs[page] = nil
52
+ end
53
+
54
+
55
+ # Setting the menu options
56
+ def menu(options = {})
57
+ active_admin_config.menu(options)
58
+ end
59
+
60
+ # Scope this controller to some object which has a relation
61
+ # to the resource. Can either accept a block or a symbol
62
+ # of a method to call.
63
+ #
64
+ # Eg:
65
+ #
66
+ # ActiveAdmin.register Post do
67
+ # scope_to :current_user
68
+ # end
69
+ #
70
+ # Then every time we instantiate and object, it would call
71
+ #
72
+ # current_user.posts.build
73
+ #
74
+ # By default Active Admin will use the resource name to build a
75
+ # method to call as the association. If its different, you can
76
+ # pass in the association_method as an option.
77
+ #
78
+ # scope_to :current_user, :association_method => :blog_posts
79
+ #
80
+ # will result in the following
81
+ #
82
+ # current_user.blog_posts.build
83
+ #
84
+ def scope_to(*args, &block)
85
+ options = args.extract_options!
86
+ method = args.first
87
+
88
+ active_admin_config.scope_to = block_given? ? block : method
89
+ active_admin_config.scope_to_association_method = options[:association_method]
90
+ end
91
+
92
+ #
93
+ # Index Config
94
+ #
95
+
96
+ # Configure the index page for the resource
97
+ def index(options = {}, &block)
98
+ options[:as] ||= :table
99
+ set_page_config :index, ActiveAdmin::PageConfig.new(options, &block)
100
+ end
101
+
102
+ # Configure the show page for the resource
103
+ def show(options = {}, &block)
104
+ set_page_config :show, ActiveAdmin::PageConfig.new(options, &block)
105
+ end
106
+
107
+ # Define the getting and re-setter for each configurable page
108
+ [:index, :show].each do |page|
109
+ # eg: index_config
110
+ define_method :"#{page}_config" do
111
+ get_page_config(page)
112
+ end
113
+
114
+ # eg: reset_index_config!
115
+ define_method :"reset_#{page}_config!" do
116
+ reset_page_config! page
117
+ end
118
+ end
119
+
120
+
121
+ #
122
+ # Form Config
123
+ #
124
+
125
+ def form(options = {}, &block)
126
+ options[:block] = block
127
+ self.form_config = options
128
+ end
129
+
130
+ def form_config
131
+ read_inheritable_attribute(:form_config) || default_form_config
132
+ end
133
+
134
+ def reset_form_config!
135
+ self.form_config = nil
136
+ end
137
+
138
+ def default_form_config
139
+ config = {}
140
+ config[:block] = lambda do |f|
141
+ f.inputs
142
+ f.buttons
143
+ end
144
+ config
145
+ end
146
+
147
+ end
148
+
149
+ # Default Sidebar Sections
150
+ sidebar :filters, :only => :index do
151
+ active_admin_filters_form_for @search, filters_config
152
+ end
153
+
154
+ # Default Action Item Links
155
+ action_item :only => :show do
156
+ if controller.public_methods.include?('edit')
157
+ link_to "Edit #{active_admin_config.resource_name}", edit_resource_path(resource)
158
+ end
159
+ end
160
+
161
+ action_item :only => :show do
162
+ if controller.public_methods.include?("destroy")
163
+ link_to "Delete #{active_admin_config.resource_name}",
164
+ resource_path(resource),
165
+ :method => :delete, :confirm => "Are you sure you want to delete this?"
166
+ end
167
+ end
168
+
169
+ action_item :except => [:new, :show] do
170
+ if controller.public_methods.include?('new')
171
+ link_to "New #{active_admin_config.resource_name}", new_resource_path
172
+ end
173
+ end
174
+
175
+ #
176
+ # Actions
177
+ #
178
+
179
+ def index
180
+ index! do |format|
181
+ format.html { render_or_default 'index' }
182
+ format.csv {
183
+ @csv_columns = resource_class.columns.collect{ |column| column.name.to_sym }
184
+ render_or_default 'index'
185
+ }
186
+ end
187
+ end
188
+
189
+ private
190
+
191
+ def collection
192
+ get_collection_ivar || set_collection_ivar(active_admin_collection)
193
+ end
194
+
195
+ def active_admin_collection
196
+ chain = scoped_collection
197
+ chain = sort_order(chain)
198
+ chain = search(chain)
199
+ chain = paginate(chain)
200
+ chain
201
+ end
202
+
203
+ # Override this method in your controllers to modify the start point
204
+ # of our searches and index.
205
+ #
206
+ # This method should return an ActiveRecord::Relation object so that
207
+ # the searching and filtering can be applied on top
208
+ def scoped_collection
209
+ end_of_association_chain
210
+ end
211
+
212
+ def begin_of_association_chain
213
+ return nil unless active_admin_config.scope_to
214
+ case active_admin_config.scope_to
215
+ when Proc
216
+ instance_eval &active_admin_config.scope_to
217
+ when Symbol
218
+ send active_admin_config.scope_to
219
+ else
220
+ raise ArgumentError, "#scope_to accepts a symbol or a block"
221
+ end
222
+ end
223
+
224
+ # Overriding from InheritedResources::BaseHelpers
225
+ #
226
+ # Returns the method for the association chain when using
227
+ # the scope_to option
228
+ def method_for_association_chain
229
+ active_admin_config.scope_to_association_method || super
230
+ end
231
+
232
+ # Allow more records for csv files
233
+ def setup_pagination_for_csv
234
+ @per_page = 10_000 if request.format == 'text/csv'
235
+ end
236
+
237
+ def paginate(chain)
238
+ chain.paginate(:page => params[:page], :per_page => @per_page || ActiveAdmin.default_per_page)
239
+ end
240
+
241
+ def sort_order(chain)
242
+ params[:order] ||= active_admin_config.sort_order
243
+ if params[:order] && params[:order] =~ /^([\w\_\.]+)_(desc|asc)$/
244
+ chain.order("#{$1} #{$2}")
245
+ else
246
+ chain # just return the chain
247
+ end
248
+ end
249
+
250
+ def search(chain)
251
+ @search = chain.search(clean_search_params(params[:q]))
252
+ end
253
+
254
+ def clean_search_params(search_params)
255
+ return {} unless search_params.is_a?(Hash)
256
+ search_params = search_params.dup
257
+ search_params.delete_if do |key, value|
258
+ value == ""
259
+ end
260
+ search_params
261
+ end
262
+
263
+ # Gets called as a before filter to set the section's breadcrumb
264
+ def add_section_breadcrumb
265
+ add_breadcrumb active_admin_config.plural_resource_name, collection_path
266
+ end
267
+
268
+ # Set's @current_tab to be name of the tab to mark as current
269
+ # Get's called through a before filter
270
+ def set_current_tab
271
+ @current_tab = active_admin_config.parent_menu_item_name ||
272
+ active_admin_config.menu_item_name
273
+ end
274
+
275
+ def active_admin_config
276
+ self.class.active_admin_config
277
+ end
278
+ helper_method :active_admin_config
279
+
280
+ def index_config
281
+ @index_config ||= self.class.index_config
282
+ end
283
+ helper_method :index_config
284
+
285
+ def show_config
286
+ @show_config ||= self.class.show_config
287
+ end
288
+ helper_method :show_config
289
+
290
+ def form_config
291
+ @form_config ||= self.class.form_config
292
+ end
293
+ helper_method :form_config
294
+
295
+ def current_menu
296
+ active_admin_config.namespace.menu
297
+ end
298
+ helper_method :current_menu
299
+
300
+ def skip_sidebar!
301
+ @skip_sidebar = true
302
+ end
303
+ helper_method :skip_sidebar!
304
+
305
+ def skip_sidebar?
306
+ @skip_sidebar == true
307
+ end
308
+ helper_method :skip_sidebar?
309
+
310
+ # Returns the renderer class to use for the given action.
311
+ #
312
+ # TODO: This needs to be wrapped into a default config as well
313
+ # as overrideable on each controller
314
+ def renderer_for(action)
315
+ {
316
+ :index => ::ActiveAdmin::Pages::Index,
317
+ :new => ::ActiveAdmin::Pages::New,
318
+ :show => ::ActiveAdmin::Pages::Show,
319
+ :edit => ::ActiveAdmin::Pages::Edit
320
+ }[action]
321
+ end
322
+ helper_method :renderer_for
323
+
324
+ end
325
+ end
@@ -0,0 +1,78 @@
1
+ require 'active_admin/helpers/optional_display'
2
+
3
+ module ActiveAdmin
4
+ module Sidebar
5
+
6
+ def self.included(base)
7
+ base.send :extend, ClassMethods
8
+ base.class_inheritable_accessor :sidebar_sections
9
+ base.sidebar_sections = []
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ def sidebar(name, options = {}, &block)
15
+ self.sidebar_sections << ActiveAdmin::Sidebar::Section.new(name, options, &block)
16
+ end
17
+
18
+ def clear_sidebar_sections!
19
+ self.sidebar_sections = []
20
+ end
21
+
22
+ def sidebar_sections_for(action)
23
+ sidebar_sections.select{|section| section.display_on?(action) }
24
+ end
25
+ end
26
+
27
+ class Section
28
+ include ActiveAdmin::OptionalDisplay
29
+
30
+ attr_accessor :name, :options, :block
31
+
32
+ def initialize(name, options = {}, &block)
33
+ @name, @options, @block = name, options, block
34
+ normalize_display_options!
35
+ end
36
+
37
+ # The id gets used for the div in the view
38
+ def id
39
+ name.to_s.downcase.underscore + '_sidebar_section'
40
+ end
41
+
42
+ # The title gets displayed within the section in the view
43
+ def title
44
+ name.to_s.titlecase
45
+ end
46
+
47
+ # If a block is not passed in, the name of the partial to render
48
+ def partial_name
49
+ options[:partial] || "#{name.to_s.downcase.gsub(' ', '_')}_sidebar"
50
+ end
51
+ end
52
+
53
+ class Renderer < ActiveAdmin::Renderer
54
+ def to_html(sidebar_sections)
55
+ sidebar_sections.collect do |section|
56
+ title = content_tag :h3, section.title
57
+ content = content_tag :div, sidebar_content(section)
58
+
59
+ content_tag :div, :class => 'sidebar_section', :id => section.id do
60
+ title + content
61
+ end
62
+ end.join
63
+ end
64
+
65
+ # If a block exists, render the block. Otherwise render a partial
66
+ def sidebar_content(section)
67
+ if section.block
68
+ instance_eval(&section.block)
69
+ else
70
+ capture do
71
+ render section.partial_name
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,162 @@
1
+ module ActiveAdmin
2
+ class TableBuilder
3
+
4
+ attr_reader :columns
5
+
6
+ def initialize
7
+ @columns = []
8
+ yield self if block_given?
9
+ end
10
+
11
+ def column(*args, &block)
12
+ @columns << Column.new(*args, &block)
13
+ end
14
+
15
+ # This method allows you to add many columns at
16
+ # once or returns the list of all columns.
17
+ #
18
+ # TableBuilder.new do |t|
19
+ # t.columns :first, :second, :third
20
+ # end
21
+ #
22
+ # OR
23
+ #
24
+ # builder.columns #=> [:first, :second, :third]
25
+ def columns(first_column = nil, *more)
26
+ return @columns unless first_column
27
+ [first_column, more].flatten.each do |c|
28
+ column(c)
29
+ end
30
+ end
31
+
32
+ # Helper method to quickly render a table builder
33
+ def to_html(view, collection, options)
34
+ Renderer.new(view).to_html(self, collection, options)
35
+ end
36
+
37
+ class Renderer < ActiveAdmin::Renderer
38
+ def to_html(builder, collection, options = {})
39
+ @builder, @collection = builder, collection
40
+ @sortable = options.delete(:sortable) || false
41
+ table_options = {
42
+ :border => 0,
43
+ :cellpadding => 0,
44
+ :cellspacing => 0
45
+ }.merge(options)
46
+ content_tag :table, table_options do
47
+ table_head + table_body
48
+ end
49
+ end
50
+
51
+ def columns
52
+ @columns ||= @builder.columns.select do |column|
53
+ instance_eval &column.conditional_block
54
+ end
55
+ end
56
+
57
+ def sortable?
58
+ @sortable
59
+ end
60
+
61
+ def table_head
62
+ content_tag :thead do
63
+ columns.collect do |column|
64
+ if sortable? && column.sortable?
65
+ sortable_header_for column.title, column.sort_key
66
+ else
67
+ content_tag :th, column.title
68
+ end
69
+ end.join
70
+ end
71
+ end
72
+
73
+ def table_body
74
+ content_tag :tbody do
75
+ @collection.collect{|item| table_row(item) }.join
76
+ end
77
+ end
78
+
79
+ def table_row(item)
80
+ content_tag :tr, :class => cycle('odd', 'even') do
81
+ columns.collect{|column| table_cell(column, item) }.join
82
+ end
83
+ end
84
+
85
+ def resource
86
+ @resource
87
+ end
88
+
89
+ def table_cell(column, item)
90
+ row_content = call_method_or_proc_on(item, column.data) || ""
91
+ l(row_content) if [Date, DateTime, Time].include?(row_content)
92
+ content_tag :td, row_content
93
+ end
94
+
95
+ end
96
+
97
+ class Column
98
+
99
+ attr_accessor :title, :data
100
+
101
+ def initialize(*args, &block)
102
+ @options = default_options.merge(args.last.is_a?(::Hash) ? args.pop : {})
103
+ @title = pretty_title args[0]
104
+ @data = args[1] || args[0]
105
+ @data = block if block
106
+ end
107
+
108
+ def sortable?
109
+ if @data.is_a?(Proc)
110
+ [String, Symbol].include?(@options[:sortable].class)
111
+ else
112
+ @options[:sortable]
113
+ end
114
+ end
115
+
116
+ #
117
+ # Returns the key to be used for sorting this column
118
+ #
119
+ # Defaults to the column's method if its a symbol
120
+ # column :username
121
+ # # => Sort key will be set to 'username'
122
+ #
123
+ # You can set the sort key by passing a string or symbol
124
+ # to the sortable option:
125
+ # column :username, :sortable => 'other_column_to_sort_on'
126
+ #
127
+ # If you pass a block to be rendered for this column, the column
128
+ # will not be sortable unless you pass a string to sortable to
129
+ # sort the column on:
130
+ #
131
+ # column('Username', :sortable => 'login'){ @user.pretty_name }
132
+ # # => Sort key will be 'login'
133
+ #
134
+ def sort_key
135
+ if @options[:sortable] == true || @options[:sortable] == false
136
+ @data.to_s
137
+ else
138
+ @options[:sortable].to_s
139
+ end
140
+ end
141
+
142
+ def conditional_block
143
+ @options[:if]
144
+ end
145
+
146
+ private
147
+
148
+ def pretty_title(raw)
149
+ raw.is_a?(Symbol) ? raw.to_s.split('_').collect{|s| s.capitalize }.join(' ') : raw
150
+ end
151
+
152
+ def default_options
153
+ {
154
+ :sortable => true,
155
+ :if => proc{ true }
156
+ }
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+ end