root/trunk/auto-admin/README

Revision 1, 16.9 kB (checked in by matthew, 2 years ago)

Initial import, from 0.0 release tarballs.

Line 
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 Django
7 [1] administration system, and the only theme currently available is
8 based directly on Django's administration system. From the screenshots
9 posted so far, it appears to share goals with Streamlined [2].
10
11   [1] Django:      http://www.djangoproject.com/
12   [2] Streamlined: http://streamlined.relevancellc.com/
13
14
15 Example?
16
17   class Customer < ActiveRecord::Base
18     belongs_to :store
19     has_many :payments, :order => 'payment_date DESC'
20
21     def name; first_name + ' ' + last_name; end
22
23     sort_by :last_name
24     search_by :first_name, :last_name
25     filter_by :active, :store
26     default_filter :active => true
27     list_columns :store, :first_name, :last_name
28
29     admin_fieldset do |b|
30       b.text_field :first_name
31       b.text_field :last_name
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   Results in:
41     http://trebex.net/~matthew/auto-admin-0.0/list.png
42     http://trebex.net/~matthew/auto-admin-0.0/edit.png
43
44
45 What isn't it?
46
47 Scaffolding. This is not a view generator for you to then customise.
48 Either it provides the interface you want, or it doesn't. (With a
49 limited, but hopefully expanding, set of exceptions.)
50
51 For everyone. This is for applications that have a public interface and
52 a restricted-access administrative interface. Its goal is not to
53 generate views you would otherwise have to craft manually, so much as
54 generating views you otherwise wouldn't bother to create. Of course, a
55 neat side-effect of using this is that your boss (or your client's IT
56 manager) can make simple database-level changes that would otherwise
57 require a developer to use either the console or direct SQL. If you're
58 trying to create an interface for all your users, this probably isn't
59 for you.
60
61
62 Where is it?
63
64 Right now, there's just a tarball available at
65 http://trebex.net/~matthew/auto-admin-0.0/auto-admin-0.0.tar.gz.
66
67 I need to get a public SVN repository set up for it, and populate a
68 useful Web page. Writing this has at least given me some material to
69 that end.
70
71
72 Is it usable?
73
74 Perhaps, but probably not quite yet. It currently doesn't like editable
75 sublists, for one, and it lacks a reliable set of tests... I've TDDed a
76 few features, but the tests covering the rest of the functionality are
77 rather sparse.
78
79 I'm releasing mostly for selfish reasons: I'm hoping that publishing the
80 code will shame me into fixing the nasty bits. :)
81
82 Other, more pressing, time constraints are forcing this release before I
83 get it cleaned up as much as I'd like (hence the lack of public SVN or a
84 website). An unfortunate side-effect of this is the timing with respect
85 to Streamlined's release. On that note, I'll be looking closely at
86 Streamlined upon its release, probably with a view to moving any useful
87 functionality I've built here into it; Justin and Stuart have far more
88 Rails experience than I do, and I expect that fact will be heavily
89 reflected in any comparison between this plugin and Streamlined.
90
91
92 What does it assume?
93
94 All objects it encounters can be usefully represented to a human as a
95 string. It achieves this by adding a to_label method to Object, which
96 will return the first available of (label, name, to_s, or inspect).
97
98 Your access control requirements for the administration section are
99 relatively "all or nothing". I intend to add simple class- and fieldset-
100 level declarative permission checking soonish (whenever I start to need
101 it). Access control based on querying individual objects should come at
102 some point, but I don't anticipate needing that level of control any
103 time soon. You can currently customise which fields are displayed (the
104 field list is a block of code, after all), but will end up with empty
105 fieldsets if you don't include any.
106
107 If you have any access control (which I expect will pretty much always
108 be the case), you must have a User constant, it must respond to one of
109 (authenticate, login, or find_by_username_and_password), and that method
110 must take two strings, and return nil for failure or a non-false value
111 for success. It *should* return the authenticated user's model -- if the
112 returned value responds to one or more of (active?, enabled?, disabled?,
113 and admin?), they will be treated appropriately. The currently logged-in
114 user (as returned by the authentication function) will be looked for and
115 stored in session[:user], so if other parts of your site do the same,
116 things will Just Work. I'm concious of the fact that storing
117 ActiveRecord instances in the session is inadvisable, and will probably
118 change this sometime soon.
119
120
121 How do I use it?
122
123 Initially (after installing the plugin, obviously), you need to add a
124 few lines to the bottom of your environment.rb:
125   AutoAdmin.config do |admin|
126     # This information is used by the theme to construct a useful
127     # header; the first parameter is the full URL of the main site, the
128     # second is the displayed name of the site, and the third (optional)
129     # parameter is the title for the administration site.
130     admin.set_site_info 'http://www.example.tld/', 'example.tld'
131
132     # "Primary Objects" are those for which lists should be directly
133     # accessible from the home page.
134     admin.primary_objects = %w(actor film user)
135
136     admin.theme = :django # Optional; this is the default.
137   end
138
139 Having done that, you can now (re-)start script/server, and navigate to
140 http://localhost:3000/admin/. Yes, it installs its own routes. Yes,
141 they're hard-coded. Yes, that needs to change... for now, just don't try
142 to use /admin/ for anything else. :)
143
144 To customise which fields appear in the edit and list screens, you go on
145 to...
146
147
148 How does it work? - Part I, Declarative UI definition
149
150 The plugin adds a number of singleton methods to ActiveRecord::Base,
151 which permit you to declare how the administration interface should
152 behave.
153
154 This set of methods, which are quite central to the utility of the
155 plugin, have grown rather organically, over a period of time (as has my
156 Ruby-fu). I've attempted to clear out the most glaring API
157 inconsitencies, but it's still a bit of a mess. Some of the
158 implementations definitely leave a bit to be desired. Cleaning this up
159 is near the top of my TODO list. That said, it should all work. :)
160
161 I really need to go through and write decent documentation for all the
162 published methods, but for now, the following summary should at least
163 act as a guide. Essentially, inside the model, you can use the following
164 methods:
165   object_group(group_name)
166     # Declares which 'object group' this object belongs to, for use in
167     # the interface. Currently, this is used to group together related
168     # objects on the index page.
169   sort_by(column, reverse=false)
170     # Instructs the list view to sort on the specified column by
171     # default.
172   search_by(*columns)
173     # Add rudimentary text searching across the named columns. Note that
174     # this defines a MyModel.search(many, query, options={}) wrapper
175     # around MyModel.find(many, options).
176   filter_by(*columns)
177     # Allow filtering of the list screen by the named columns (filtering
178     # currently works for: custom, boolean, date, belongs_to, has_one,
179     # and string). Note that the last three will do rather nasty and
180     # sub-optimal queries to determine the filter options.
181   default_filter(filters)
182     # Takes a hash of (column, value) pairs, to default a filter to
183     # something other than 'All'.
184   filter_options_for(column, choices, &block)
185     # Specifies a fixed set of choices to be offered as filter options
186     # instead of automatically working it out. Choices should be a
187     # (value, label) hash. The optional block will be given each value
188     # in turn, and should return an SQL condition fragment.
189   column_labels(labels)
190     # Takes a hash of (column, label) pairs, to change the default label
191     # for a field to explicitly define the human label for a column.
192     # This label will be the default used in both list and edit views.
193   list_columns(*columns, &proc)
194     # Takes either a simple-list of column names, or a Field Definition
195     # Block (see below)
196   admin_fieldset(label='', *columns, &proc)
197     # Defines a fieldset for edit views. For simple use, you can just
198     # give it a list of columns. Once you get started, you'll want to
199     # pass a Field Definition Block, though.
200   admin_child_table(label, collection, options={}, &proc)
201     # Defines a fieldset for edit views, to show a table of items from a
202     # child collection. It uses a Field Definition Block to declare what
203     # columns should be shown. Generally, you'd want to use the
204     # static_text helper, I suspect.
205     # WARNING: This has no tests, and I'm almost certain it will break
206     # horribly if you try to use anything other than static_text.
207   admin_child_form(collection, options={}, &proc)
208     # Defines a "fieldset" for edit views, to show *several* fieldsets,
209     # each containing one object from a child collection. It uses a
210     # Field Definition Block to declare what columns should be shown.
211     # I don't think it'd be wise to use this on a large collection, but
212     # it's your application. :)
213     # WARNING: This also has no tests, and I believe it will break
214     # horribly if you try to use it at all.
215
216 Field Definition Block?!?
217
218 A number of the above methods provide for a block to declare what fields
219 are to be shown. This is achieved by yielding a builder to the block.
220 Depending on context, the mood of a theme author, and the phase of the
221 moon, a given block will see several builders in its lifetime. Not all
222 builders will have an active object; all will respond to the +object+
223 method, though. A basic field definition block will just call a field
224 helper on the builder for each field that it wishes to display. The
225 +auto_field+ helper (which automatically determines an appropriate field
226 type based on column and association metadata) is available if you only
227 want to specify the field type for some of the fields. All field helpers
228 take (field_name, options={}, *other_stuff). Most just take the two
229 parameters, and I'm considering deprecating the extra parameters on
230 those that currently support them. Note that unlike a standard builder,
231 you don't have to do anything with the return value; the theme's actual
232 FormBuilder is wrapped by a DeclarativeFormBuilder, which takes care of
233 that for you.
234
235 In theory, there's no compelling reason you can't add complex logic to a
236 field definition block, such as examining the current user, or even the
237 builder's active object (though I strongly encourage you to handle nil
238 permissively, at this stage). It would be unwise to vary the fields
239 returned based on the object for a list view, for fairly obvious
240 reasons.
241
242 Available Form Helpers
243
244 Simple helpers that just delegate to the ActionView's FormBuilder:
245   hidden_field, date_select, datetime_select, text_field, text_area,
246   check_box
247
248 +select+ and +radio_group+ operate in basically the same way; they both
249 provide a method of selecting one out of several choices (ignoring
250 select :multiple, that is). Note that select's list of choices, normally
251 the second parameter to the select helper, has been relegated to a
252 :choices entry in the options, for API consistency.
253
254 +static_text+ just outputs an HTML-escaped string representation of the
255 field's value. It is useful both for read-only fields in forms, and as
256 the primary helper in lists.
257
258 +auto_field+, as discussed above, will automatically select a suitable
259 field helper, based on the column and association metadata. Where there
260 are multiple suitable candidates, it tries to go for the more
261 generally-applicable choice (for example, it favours a +select+ over a
262 +radio_group+ for a belongs_to association).
263
264 None of the following actually work, but they're defined, waiting for me
265 to come back and write them. +html_area+ will eventually use FCKeditor
266 by default, and presumably the file/image fields will delegate to
267 file_column.
268   html_area, hyperlink, file_field, image_field, static_image,
269   static_file, static_html
270
271
272 How does it work? - Part II, Themes
273
274 The theme bundled with the plugin is named 'django'; all credit for its
275 excellent appearance goes to the Django project. I hope we can get a
276 couple of standard themes, but they won't be coming from me...
277 experience shows that I shouldn't try to make things look good. I
278 believe I've successfully drawn lines in all the right places for what
279 is in the plugin's core, and what's in a theme. I've already developed
280 most of a second theme (which will not be released) for my employer, so
281 the infrastructure is mostly proven. A more coherent HOWTO on creating
282 themes (which can just be installed as seperate Rails plugins, then
283 selected in environment.rb) will be forthcoming Real Soon Now, though
284 this section has ended up covering most of the basics.
285
286 The 30 second summary -- a theme comprises:
287   FormBuilder (subclass of AutoAdminSimpleTheme::FormBuilder), to create
288   an Edit screen (a real form)
289  
290   TableBuilder (subclass of AutoAdmin::TableBuilder(FormBuilder)), to
291   create a List screen (a creative interpretation of "form", which seems
292   to map surprisingly well, for now).
293  
294   FormProcessor (subclass of AutoAdminSimpleTheme::FormProcessor), which
295   implements the same set of helper methods as the FormBuilders, but
296   instead of returning HTML, its job is to perform any transformations
297   on the params hash to correspond with unusual form field
298   representations -- the base FormProcessor transforms keys referencing
299   associations to reference the underlying columns (actor -> actor_id),
300   for example. This class will often be empty, especially once I provide
301   a facility with which to inject custom field helpers (for composed_of
302   and maybe some belongs_to, mostly) into the base builder and
303   processor.
304
305   A complete set of views, including a layout, which delegate the hard
306   work to the FormBuilders.
307
308   A 'public' directory, containing any required image, javascript, and
309   stylesheet assets.
310
311   A wrapper module, AutoAdmin#{name}Theme, which is responsible for:
312     * Containing the FormBuilders and FormProcessor
313     * Returning the full filesystem path to the 'views' and 'public'
314       directories
315     * Returing any theme-specific helpers, for injection into the
316       controller
317     * Injecting any theme-specific includes for ActiveRecord::Base
318       (I've proven this to be possible, though can't think of a sane
319       reason a theme would want to do so)
320
321 Extending your theme module with AutoAdmin::ThemeHelpers will help to
322 keep the module fairly DRY; it provides a +helper+ method, which can be
323 given a list of modules and/or a block, and directs the 'view_directory'
324 and 'asset_root' methods to a directory(*subdirs) singleton method,
325 which you must define -- presumably using __FILE__.
326
327 NB: For good reasons that I can't remember right now, a couple of helper
328 methods have APIs that don't match the standard Rails FormBuilder,
329 despite matching names.  The one that comes to mind is +select+ -- the
330 choices have been moved into the options hash, to keep all method
331 signatures of the form (field_name, options, *other_stuff).
332
333
334 What's planned, but missing?
335
336 The ability for the application to inject custom field types into the
337 base FormBuilder and FormProcessor. The theme-specific versions of these
338 classes are available so that, for example, a theme can decide how a
339 date_field should be presented, and can correspondingly recover the
340 values from multiple inputs... they don't map as well to an
341 application's requirement for a 'currency' field. Of course, there's
342 nothing stopping an application re-opening the classes and adding an
343 appropriate helper method to each... there's just a bit of undesirable
344 complexity involved if you want auto_field to detect and use it (which
345 suggests to me that auto_field needs a bit of a rethink).
346
347 A way for the application to reliably extend the AutoAdminController,
348 and add appropriate views somewhere, for those occasions when you have a
349 couple of screens that need to be hand-crafted, such as a statistics
350 display, or a particular edit screen that needs a specialised workflow.
351 Note that if you feel this constraint too much, you're probably pushing
352 the plugin into a role it doesn't fit.
353
354 Simple methods allowing an application to add navigation options, and
355 perhaps the ability to insert Components into the "dashboard" on the
356 index page?
357
358 A top-level "menu", containing links to the primary object lists by
359 default, that a theme can permanently display.
360
361 It's probably a better idea to store the logged-in user's id, instead of
362 the user object, in the session.
363
364
365 Longer-term architectural considerations?
366
367 After starting off defining the administration interfaces directly in
368 the models (as Django does), I was strongly considering moving them all
369 into an application-specific controller, that would subclass
370 AutoAdminController. I haven't gotten around to doing that, and am now
371 quite intruiged by the approach taken by Streamlined -- adding a new
372 type of class. Any such move is primarily aimed at solving a problem I'm
373 not yet sufferring, though, so for now it's just a topic to ponder.
374
375
376
377
Note: See TracBrowser for help on using the browser.