Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 515 lines (383 sloc) 22.34 kB
562cd41 @baldowl Renamed the readme.
authored
1 = Rails AutoAdmin Plugin
2
3 == What is it?
4
5 A plugin for Ruby on Rails that automagically creates an administration
6 interface, based on your models. It is heavily inspired by the
7 Django[http://www.djangoproject.com/] administration system, and the only
8 theme currently available is based directly on Django's administration system.
9 From the screenshots posted so far, it appears to share goals with
10 Streamlined[http://streamlined.relevancellc.com/].
11
12
13 == Example?
14
15 class Customer < ActiveRecord::Base
16 belongs_to :store
17 has_many :payments, :order => 'payment_date DESC'
18
19 def name; first_name + ' ' + last_name; end
20
21 refresh_time 5
22 sort_by :last_name
23 search_by :first_name, :last_name
24 filter_by :active, :store
25 default_filter :active => true
26 list_columns :store, :first_name, :last_name
27
28 admin_fieldset do |b|
29 b.text_field :first_name
30 b.text_field :last_name
31 b.auto_field :active
32 b.select :store
33 end
34 admin_child_table 'Payments', :payments do |b|
35 b.static_text :payment_date
36 b.static_text :amount
37 end
38 end
39
40
41 == What isn't it?
42
43 Scaffolding. This is not a view generator for you to then customise. Either it
44 provides the interface you want, or it doesn't. (With a limited, but hopefully
45 expanding, set of exceptions.)
46
47 For everyone. This is for applications that have a public interface and a
48 restricted-access administrative interface. Its goal is not to generate views
49 you would otherwise have to craft manually, so much as generating views you
50 otherwise wouldn't bother to create. Of course, a neat side-effect of using
51 this is that your boss (or your client's IT manager) can make simple
52 database-level changes that would otherwise require a developer to use either
53 the console or direct SQL. If you're trying to create an interface for all
54 your users, this probably isn't for you.
55
56
57 == Where is it?
58
f18d451 @baldowl Amended doc about the original code.
authored
59 The public Git repository is hosted by Github[http://github.com] and can be
562cd41 @baldowl Renamed the readme.
authored
60 reached starting from http://github.com/baldowl/auto_admin
61
62 I cannot stress enough that this is just a fork of the original code written
f18d451 @baldowl Amended doc about the original code.
authored
63 by Matthew Draper; the original code was available from the Subversion
64 repository at http://svn.trebex.net/auto-admin/trunk/auto-admin but you can
65 get it also from this Git repository checking out the +matthew+ tag.
562cd41 @baldowl Renamed the readme.
authored
66
67
68 == What does it assume?
69
70 All objects it encounters can be usefully represented to a human as a string.
71 It achieves this by adding a +to_label+ method to +Object+, which will return
72 the first available of +label+, +name+, +to_s+ or +inspect+.
73
74 Your access control requirements for the administration section are relatively
75 "all or nothing". I intend to add simple class- and fieldset- level
76 declarative permission checking soonish (whenever I start to need it). Access
77 control based on querying individual objects should come at some point, but I
78 don't anticipate needing that level of control any time soon. You can
79 currently customise which fields are displayed (the field list is a block of
80 code, after all), but will end up with empty fieldsets if you don't include
81 any.
82
83 If you have any access control (which I expect will pretty much always be the
84 case), you must specify it with the <tt>admin_model=</tt> and
85 <tt>admin_model_id=</tt> methods in the configuration block of AutoAdmin; the
86 class must respond to +authenticate+, +login+, or
87 +find_by_username_and_password+ and that method must take two strings and
88 return +nil+ for failure or a non-false value for success. It *must* return
89 the authenticated user's id (or, in a less ideal turn, the user object itself:
90 we will extract the id by ourself) -- the id will be stored in the session
91 using the key provided by +admin_model_id+ and the currently logged-in user
92 will be looked for; if the returned value responds to one or more of
93 <tt>active?</tt>, <tt>enabled?</tt>, <tt>disabled?</tt> or <tt>admin?</tt>,
94 they will be treated appropriately. So if other parts of your site do the
95 same, things will Just Work.
96
97
98 === History
99
100 Admins' actions are automatically recorded and shown if the optional
101 <tt>admin_histories</tt> table is available.
102
103 See AutoAdminController#history for details.
104
105
106 == What do you need?
107
108 To use AutoAdmin with Rails 2.* you need an extra plugin,
109 will_paginate[http://github.com/mislav/will_paginate], because the pagination
110 mechanism has been removed from the Rails core base.
111
112 To use the +text_field_with_auto_complete+ helper you need an extra plugin,
113 auto_complete[http://github.com/rails/auto_complete], because the
114 autocompletion mechanism has been removed from the Rails core base.
115
116 For the optional export mechanism you also need:
117
118 * the *faster_csv* gem for the CSV export module;
119
120 * the <b>pdf-writer</b> gem for the PDF export module.
121
046e1fa @baldowl html_area use, optionally, FCKEditor.
authored
122 To use the +html_area+ helper with FCKEditor you need an extra
123 plugin[http://rubyforge.org/projects/fckeditorp/]; see its documentation for
124 further instructions.
125
562cd41 @baldowl Renamed the readme.
authored
126
127 == How do I use it?
128
129 Initially (after installing the plugin, obviously), you need to add a few
130 lines to the bottom of your environment.rb or in an initializer file:
131
132 AutoAdmin.config do |admin|
133 # This information is used by the theme to construct a useful
134 # header; the first parameter is the full URL of the main site, the
135 # second is the displayed name of the site, and the third (optional)
136 # parameter is the title for the administration site.
137 admin.set_site_info 'http://www.example.com/', 'example.com',
138 'Administration area for example.com'
139
140 # "Primary Objects" are those for which lists should be directly
141 # accessible from the home page.
142 admin.primary_objects = %w(actor film user)
143
144 admin.theme = :django # Optional; this is the default.
145
146 # The configurable, optional access control system.
147 admin.admin_model = Account
148 admin.admin_model_id = :account_id
149
150 # The optional export mechanism.
151 admin.save_as = %w(pdf csv)
046e1fa @baldowl html_area use, optionally, FCKEditor.
authored
152
153 # Turn on the use of FCKEditor.
154 admin.use_fckeditor_plugin = true
562cd41 @baldowl Renamed the readme.
authored
155 end
156
157 Having done that, you can now (re-)start <tt>script/server</tt>, and navigate
158 to http://localhost:3000/admin/. Yes, it installs its own routes, but they are
159 partially configurable; for now, just don't try to use <tt>/admin/</tt> for
160 anything else.
161
162 To customise which fields appear in the edit and list screens, you go on to...
163
164
165 == How does it work? - Part I, Declarative UI definition
166
167 The plugin adds a number of singleton methods to ActiveRecord::Base, which
168 permit you to declare how the administration interface should behave.
169
170 This set of methods, which are quite central to the utility of the plugin,
171 have grown rather organically, over a period of time (as has my Ruby-fu). I've
172 attempted to clear out the most glaring API inconsitencies, but it's still a
173 bit of a mess. Some of the implementations definitely leave a bit to be
174 desired. Cleaning this up is near the top of my TODO list. That said, it
175 should all work. :)
176
177 I really need to go through and write decent documentation for all the
178 published methods, but for now, the following summary should at least act as a
179 guide. Essentially, inside the model, you can use the following methods:
180
181 [object_group(group_name)]
182 Declares which 'object group' this object belongs to, for use in the
183 interface. Currently, this is used to group together related objects on the
184 index page.
185
186 [refresh_time(seconds)]
187 Instructs the list view to meta-refresh with the specified delay.
188
189 [sort_by(column, reverse=false)]
190 Instructs the list view to sort on the specified column by default.
191
192 [search_by(*columns)]
193 Add rudimentary text searching across the named columns. Note that this
194 defines a <tt>MyModel.search(many, query, options={})</tt> wrapper around
195 <tt>MyModel.find(many, options)</tt>.
196
197 [filter_by(*columns)]
198 Allow filtering of the list screen by the named columns (filtering currently
199 works for: custom, boolean, date, belongs_to, has_one, and string). Note
200 that the last three will do rather nasty and sub-optimal queries to
201 determine the filter options.
202
203 [default_filter(filters)]
204 Takes a hash of (column, value) pairs, to default a filter to something
205 other than 'All'.
206
207 [filter_options_for(column, choices, &block)]
208 Specifies a fixed set of choices to be offered as filter options instead of
209 automatically working it out. Choices should be a (value, label) hash. The
210 optional block will be given each value in turn, and should return an SQL
211 condition fragment.
212
213 [column_labels(labels)]
214 Takes a hash of (column, label) pairs, to change the default label for a
215 field to explicitly define the human label for a column. This label will be
216 the default used in both list and edit views.
217
218 [list_columns(*columns, &proc)]
219 Takes either a simple-list of column names, or a Field Definition Block (see
220 next section) or both.
221
222 Please, note that a list of column names will produce a bunch of fields
223 using the +static_text+ helper almost for everything but the first column
224 (which will get a special treatment to allow you to access the "edit view").
225 If a column occurs more than once (summing up the list of column names and
226 the FDB), then that column will appear more than once:
227
228 list_columns :name, :code, :address do |f|
229 f.hyperlink :address
230 end
231
232 The previous code fragment will list the +address+ column twice: the first
233 one will be shown as static text, the second one will produce a real link.
234
235 At the moment, if you have a long list of columns and want to customize only
236 a few of them, you have to write then down one by one, even those that don't
237 need special care.
238
239 [admin_fieldset(label='', *columns, &proc)]
240 Defines a fieldset for edit views. For simple use, you can just give it a
241 list of columns. Once you get started, you'll want to pass a Field
242 Definition Block, though.
243
244 In the Field Definition Block you can get hold of the model instance using
245 the block's parameter; let's say the model has an attachment, handled by
246 paperclip[http://github.com/thoughtbot/paperclip], called +picture+ and you
247 want to show the image's URL like an hyperlink:
248
249 admin_fieldset do |f|
250 # ...
251 f.hyperlink :picture, :url => f.object.picture.url
252 end
253
254 At the moment, this does not work with +list_columns+.
255
256 Most of the helpers which accept or require a block will yield the model
257 instance to the block, if it requires a parameter:
258
259 admin_fieldset do |f|
260 # ...
261 f.static_image :picture do |cover|
262 {:src => cover.picture.url}
263 end
264 end
265
266 This works also with +list_columns+.
267
268 [admin_child_table(label, collection, options={}, &proc)]
269 Defines a fieldset for edit views, to show a table of items from a child
270 collection. It uses a Field Definition Block to declare what columns should
271 be shown. Generally, you'd want to use the static_text helper, I suspect.
272 *WARNING*: This has no tests, and I'm almost certain it will break horribly
273 if you try to use anything other than static_text.
274
275 [admin_child_form(collection, options={}, &proc)]
276 Defines a "fieldset" for edit views, to show *several* fieldsets, each
277 containing one object from a child collection. It uses a Field Definition
278 Block to declare what columns should be shown. I don't think it'd be wise to
279 use this on a large collection, but it's your application. :) *WARNING*:
280 This also has no tests, and I believe it will break horribly if you try to
281 use it at all.
282
283
284 == Field Definition Block?!?
285
286 A number of the above methods provide for a block to declare what fields are
287 to be shown. This is achieved by yielding a builder to the block. Depending on
288 context, the mood of a theme author, and the phase of the moon, a given block
289 will see several builders in its lifetime. Not all builders will have an
290 active object; all will respond to the +object+ method, though. A basic field
291 definition block will just call a field helper on the builder for each field
292 that it wishes to display. The +auto_field+ helper (which automatically
293 determines an appropriate field type based on column and association metadata)
294 is available if you only want to specify the field type for some of the
295 fields. All field helpers take <tt>(field_name, options={},
296 *other_stuff)</tt>. Most just take the two parameters, and I'm considering
297 deprecating the extra parameters on those that currently support them. Note
298 that unlike a standard builder, you don't have to do anything with the return
299 value; the theme's actual FormBuilder is wrapped by a DeclarativeFormBuilder,
300 which takes care of that for you.
301
302 In theory, there's no compelling reason you can't add complex logic to a field
303 definition block, such as examining the current user, or even the builder's
304 active object (though I strongly encourage you to handle nil permissively, at
305 this stage). It would be unwise to vary the fields returned based on the
306 object for a list view, for fairly obvious reasons.
307
308
309 == Available Form Helpers
310
311 * Simple helpers that just delegate to the ActionView's FormBuilder:
312 +hidden_field+, +date_select+, +datetime_select+, +text_field+, +text_area+,
313 +check_box+, +secure_password+, +file_field+.
314
315 * +select+ and +radio_group+ operate in basically the same way; they both
316 provide a method of selecting one out of several choices (ignoring
317 <tt>select :multiple</tt>, that is). Note that select's list of choices,
318 normally the second parameter to the select helper, has been relegated to a
319 <tt>:choices</tt> entry in the options, for API consistency.
320
321 * +static_text+ just outputs an HTML-escaped string representation of the
322 field's value. It is useful both for read-only fields in forms, and as the
323 primary helper in lists.
324
325 * +calculated_text+ requires a block to which it will yield the model
326 instance; the block's product will be treated like a string and
327 HTML-escaped.
328
329 * +static_html+ is like +static_text+, but the final product will not be
330 HTML-escaped.
331
332 * +calculated_html+ is like +calculated_text+, but the final product will not
333 be HTML-escaped.
334
335 * +auto_field+, as discussed above, will automatically select a suitable field
336 helper, based on the column and association metadata. Where there are
337 multiple suitable candidates, it tries to go for the more
338 generally-applicable choice (for example, it favours a +select+ over a
339 +radio_group+ for a +belongs_to+ association).
340
bb51264 @baldowl More info on html_area helper; less typos.
authored
341 * +static_image+ sports a number of options used to build a hash suitable for
562cd41 @baldowl Renamed the readme.
authored
342 the +tag+ helper responsible for the creation of final <tt><img></tt> tag:
343
344 * <tt>:controller</tt> (default "auto_admin") and <tt>:action</tt> (default
345 "edit") allows to select a controller which should return the image based
346 upon this object's id;
347
348 * <tt>:src</tt> override the previous two options and can be used with any
349 URL, static or dynamic;
350
351 * with <tt>:size</tt> one can write the width and height used to show the
352 image (format: "XxY");
353
354 * <tt>:alt</tt>, which defaults to this object's +to_label+.
355
356 Everything else is passed as is to the +tag+ helper (so one could use
357 <tt>:style</tt> or <tt>:class</tt> to alter the looks of <tt><img></tt>).
358 This helper accepts a block, with a single parameter (this object), which
bb51264 @baldowl More info on html_area helper; less typos.
authored
359 can be used to return a hash to be merged into the options that are about to
360 be passed to the tag helper.
562cd41 @baldowl Renamed the readme.
authored
361
362 * +text_field_with_auto_complete+ provides a simple text field with
363 autocompletion delegating all the work to the homonymous ActionView's
364 helper; there's a complication, however: there must be a separate controller
365 which provides the completion data:
366
367 b.text_field_with_auto_complete :name,
368 :completion => { :url => { :controller => 'items',
369 :action => 'auto_complete_for_item_name' }}
370
371 For information on this controller, see Rails documentation. Note that if
372 you designed your application in a REST-like way, you should tweak your
373 resources' definitios or add a specific route for the new
374 <tt>auto_complete_for_<em>model</em>_<em>field</em></tt>.
375
376 * +hyperlink+ automatically generates a link to the "edit view" of its first
377 argument (which must be one of the primary objects); alternatively you can
378 use the <tt>:url</tt> option to generate a custom link:
379
380 f.hyperlink :picture, :url => f.object.picture.url
381
382 Anyway, the link caption will be the URL itself, unless you use the option
383 <tt>:link_text</tt> as follow:
384
385 f.hyperlink :picture, :url => f.object.picture.url,
386 :link_text => 'The picture'
387
046e1fa @baldowl html_area use, optionally, FCKEditor.
authored
388 * +html_area+ use FCKEditor if the support has been explicitly turned on,
bb51264 @baldowl More info on html_area helper; less typos.
authored
389 otherwise it delegates to +text_area+; when using FCKEditor, you can pass a
390 hash of options down to the editor:
391
392 f.html_area :content, :toolbarSet => 'MyStyle', :height => '300px'
393
394 See the FCKEditor plugin[http://rubyforge.org/projects/fckeditorp/]'s
395 documentation.
046e1fa @baldowl html_area use, optionally, FCKEditor.
authored
396
562cd41 @baldowl Renamed the readme.
authored
397 * None of the following actually work, but they're defined, waiting for me to
046e1fa @baldowl html_area use, optionally, FCKEditor.
authored
398 come back and write them. Presumably the image fields will delegate to
399 file_column: +image_field+, +static_file+.
562cd41 @baldowl Renamed the readme.
authored
400
401
402 == How does it work? - Part II, Themes
403
404 The theme bundled with the plugin is named 'django'; all credit for its
405 excellent appearance goes to the Django project. I hope we can get a couple of
406 standard themes, but they won't be coming from me... experience shows that I
407 shouldn't try to make things look good. I believe I've successfully drawn
408 lines in all the right places for what is in the plugin's core, and what's in
409 a theme. I've already developed most of a second theme (which will not be
410 released) for my employer, so the infrastructure is mostly proven. A more
411 coherent HOWTO on creating themes (which can just be installed as seperate
412 Rails plugins, then selected in environment.rb) will be forthcoming Real Soon
413 Now, though this section has ended up covering most of the basics.
414
415 The 30 second summary -- a theme comprises:
416
417 * FormBuilder (subclass of AutoAdminSimpleTheme::FormBuilder), to create an
418 Edit screen (a real form)
419
420 * TableBuilder (subclass of AutoAdmin::TableBuilder(FormBuilder)), to create a
421 List screen (a creative interpretation of "form", which seems to map
422 surprisingly well, for now).
423
424 * FormProcessor (subclass of AutoAdminSimpleTheme::FormProcessor), which
425 implements the same set of helper methods as the FormBuilders, but instead
426 of returning HTML, its job is to perform any transformations on the params
427 hash to correspond with unusual form field representations -- the base
428 FormProcessor transforms keys referencing associations to reference the
429 underlying columns (actor -> actor_id), for example. This class will often
430 be empty, especially once I provide a facility with which to inject custom
431 field helpers (for composed_of and maybe some belongs_to, mostly) into the
432 base builder and processor.
433
434 * A complete set of views, including a layout, which delegate the hard work to
435 the FormBuilders.
436
437 * A 'public' directory, containing any required image, javascript, and
438 stylesheet assets.
439
440 * A wrapper module, AutoAdmin#{name}Theme, which is responsible for:
441
442 * Containing the FormBuilders and FormProcessor
443
444 * Returning the full filesystem path to the 'views' and 'public' directories
445
446 * Returing any theme-specific helpers, for injection into the controller
447
448 * Injecting any theme-specific includes for ActiveRecord::Base (I've proven
449 this to be possible, though can't think of a sane reason a theme would
450 want to do so)
451
452 Extending your theme module with AutoAdmin::ThemeHelpers will help to keep the
453 module fairly DRY; it provides a +helper+ method, which can be given a list of
454 modules and/or a block, and directs the 'view_directory' and 'asset_root'
455 methods to a directory(*subdirs) singleton method, which you must define --
456 presumably using \_\_FILE\_\_.
457
458 NB: For good reasons that I can't remember right now, a couple of helper
459 methods have APIs that don't match the standard Rails FormBuilder, despite
460 matching names. The one that comes to mind is +select+ -- the choices have
461 been moved into the options hash, to keep all method signatures of the form
462 (field_name, options, *other_stuff).
463
464
465 == What's planned, but missing?
466
467 The ability for the application to inject custom field types into the base
468 FormBuilder and FormProcessor. The theme-specific versions of these classes
469 are available so that, for example, a theme can decide how a date_field should
470 be presented, and can correspondingly recover the values from multiple
471 inputs... they don't map as well to an application's requirement for a
472 'currency' field. Of course, there's nothing stopping an application
473 re-opening the classes and adding an appropriate helper method to each...
474 there's just a bit of undesirable complexity involved if you want auto_field
475 to detect and use it (which suggests to me that auto_field needs a bit of a
476 rethink).
477
478 A way for the application to reliably extend the AutoAdminController, and add
479 appropriate views somewhere, for those occasions when you have a couple of
480 screens that need to be hand-crafted, such as a statistics display, or a
481 particular edit screen that needs a specialised workflow. Note that if you
482 feel this constraint too much, you're probably pushing the plugin into a role
483 it doesn't fit.
484
485 Simple methods allowing an application to add navigation options, and perhaps
486 the ability to insert Components into the "dashboard" on the index page?
487
488 A top-level "menu", containing links to the primary object lists by default,
489 that a theme can permanently display.
490
491
492 == Longer-term architectural considerations?
493
494 After starting off defining the administration interfaces directly in the
495 models (as Django does), I was strongly considering moving them all into an
496 application-specific controller, that would subclass AutoAdminController. I
497 haven't gotten around to doing that, and am now quite intruiged by the
498 approach taken by Streamlined -- adding a new type of class. Any such move is
499 primarily aimed at solving a problem I'm not yet sufferring, though, so for
500 now it's just a topic to ponder.
41b81a8 @baldowl Added advices about contributing.
authored
501
502
503 == Contributing
504
505 If you want to help:
506
507 * fork the project[http://github.com/baldowl/auto_admin] on GitHub;
508 * work in a topic branch;
509 * write your additions/bug fixes;
510 * commit;
511 * send me a pull request for the topic branch.
512
513 If you have any issue, please post them on the {project's issue
514 list}[http://github.com/baldowl/auto_admin] on GitHub.
Something went wrong with that request. Please try again.