Skip to content
This repository
Browse code

Renamed the readme.

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

0 notes on commit 562cd41

Please sign in to comment.
Something went wrong with that request. Please try again.