auto_admin
文件大小: unknow
源码售价: 5 个金币 积分规则     积分充值
资源说明:A Git fork of the original auto_admin repository, with just a bunch of changes to scratch my own itch
= Rails AutoAdmin Plugin

== What is it?

A plugin for Ruby on Rails that automagically creates an administration
interface, based on your models. It is heavily inspired by the
Django[http://www.djangoproject.com/] administration system, and the only
theme currently available is based directly on Django's administration system.
From the screenshots posted so far, it appears to share goals with
Streamlined[http://streamlined.relevancellc.com/].


== Example?

  class Customer < ActiveRecord::Base
    belongs_to :store
    has_many :payments, :order => 'payment_date DESC'

    def name; first_name + ' ' + last_name; end

    refresh_time 5
    sort_by :last_name
    search_by :first_name, :last_name
    filter_by :active, :store
    default_filter :active => true
    list_columns :store, :first_name, :last_name

    admin_fieldset do |b|
      b.text_field :first_name
      b.text_field :last_name
      b.auto_field :active
      b.select :store
    end
    admin_child_table 'Payments', :payments do |b|
      b.static_text :payment_date
      b.static_text :amount
    end
  end


== What isn't it?

Scaffolding. This is not a view generator for you to then customise. Either it
provides the interface you want, or it doesn't. (With a limited, but hopefully
expanding, set of exceptions.)

For everyone. This is for applications that have a public interface and a
restricted-access administrative interface. Its goal is not to generate views
you would otherwise have to craft manually, so much as generating views you
otherwise wouldn't bother to create. Of course, a neat side-effect of using
this is that your boss (or your client's IT manager) can make simple
database-level changes that would otherwise require a developer to use either
the console or direct SQL. If you're trying to create an interface for all
your users, this probably isn't for you.


== Where is it?

The public Git repository is hosted by Github[http://github.com] and can be
reached starting from http://github.com/baldowl/auto_admin

I cannot stress enough that this is just a fork of the original code written
by Matthew Draper; the original code was available from the Subversion
repository at http://svn.trebex.net/auto-admin/trunk/auto-admin but you can
get it also from this Git repository checking out the +matthew+ tag.


== What does it assume?

All objects it encounters can be usefully represented to a human as a string.
It achieves this by adding a +to_label+ method to +Object+, which will return
the first available of +label+, +name+, +to_s+ or +inspect+.

Your access control requirements for the administration section are relatively
"all or nothing". I intend to add simple class- and fieldset- level
declarative permission checking soonish (whenever I start to need it). Access
control based on querying individual objects should come at some point, but I
don't anticipate needing that level of control any time soon. You can
currently customise which fields are displayed (the field list is a block of
code, after all), but will end up with empty fieldsets if you don't include
any.

If you have any access control (which I expect will pretty much always be the
case), you must specify it with the admin_model= and
admin_model_id= methods in the configuration block of AutoAdmin; the
class must respond to +authenticate+, +login+, or
+find_by_username_and_password+ and that method must take two strings and
return +nil+ for failure or a non-false value for success. It *must* return
the authenticated user's id (or, in a less ideal turn, the user object itself:
we will extract the id by ourself) -- the id will be stored in the session
using the key provided by +admin_model_id+ and the currently logged-in user
will be looked for; if the returned value responds to one or more of
active?, enabled?, disabled? or admin?,
they will be treated appropriately. So if other parts of your site do the
same, things will Just Work.


=== History

Admins' actions are automatically recorded and shown if the optional
admin_histories table is available.

See AutoAdminController#history for details.


== What do you need?

To use AutoAdmin with Rails 2.* you need an extra plugin,
will_paginate[http://github.com/mislav/will_paginate], because the pagination
mechanism has been removed from the Rails core base.

To use the +text_field_with_auto_complete+ helper you need an extra plugin,
auto_complete[http://github.com/rails/auto_complete], because the
autocompletion mechanism has been removed from the Rails core base.

For the optional export mechanism you also need:

* the *faster_csv* gem for the CSV export module;

* the pdf-writer gem for the PDF export module.

To use the +html_area+ helper with FCKEditor you need an extra
plugin[http://rubyforge.org/projects/fckeditorp/]; see its documentation for
further instructions.


== How do I use it?

Initially (after installing the plugin, obviously), you need to add a few
lines to the bottom of your environment.rb or in an initializer file:

  AutoAdmin.config do |admin|
    # This information is used by the theme to construct a useful
    # header; the first parameter is the full URL of the main site, the
    # second is the displayed name of the site, and the third (optional)
    # parameter is the title for the administration site.
    admin.set_site_info 'http://www.example.com/', 'example.com',
      'Administration area for example.com'

    # "Primary Objects" are those for which lists should be directly
    # accessible from the home page.
    admin.primary_objects = %w(actor film user)

    admin.theme = :django # Optional; this is the default.

    # The configurable, optional access control system.
    admin.admin_model = Account
    admin.admin_model_id = :account_id

    # The optional export mechanism.
    admin.save_as = %w(pdf csv)

    # Turn on the use of FCKEditor.
    admin.use_fckeditor_plugin = true
  end

Having done that, you can now (re-)start script/server, and navigate
to http://localhost:3000/admin/. Yes, it installs its own routes, but they are
partially configurable; for now, just don't try to use /admin/ for
anything else.

To customise which fields appear in the edit and list screens, you go on to...


== How does it work? - Part I, Declarative UI definition

The plugin adds a number of singleton methods to ActiveRecord::Base, which
permit you to declare how the administration interface should behave.

This set of methods, which are quite central to the utility of the plugin,
have grown rather organically, over a period of time (as has my Ruby-fu). I've
attempted to clear out the most glaring API inconsitencies, but it's still a
bit of a mess. Some of the implementations definitely leave a bit to be
desired. Cleaning this up is near the top of my TODO list. That said, it
should all work. :)

I really need to go through and write decent documentation for all the
published methods, but for now, the following summary should at least act as a
guide. Essentially, inside the model, you can use the following methods:

[object_group(group_name)]
  Declares which 'object group' this object belongs to, for use in the
  interface. Currently, this is used to group together related objects on the
  index page.

[refresh_time(seconds)]
  Instructs the list view to meta-refresh with the specified delay.

[sort_by(column, reverse=false)]
  Instructs the list view to sort on the specified column by default.

[search_by(*columns)]
  Add rudimentary text searching across the named columns. Note that this
  defines a MyModel.search(many, query, options={}) wrapper around
  MyModel.find(many, options).

[filter_by(*columns)]
  Allow filtering of the list screen by the named columns (filtering currently
  works for: custom, boolean, date, belongs_to, has_one, and string). Note
  that the last three will do rather nasty and sub-optimal queries to
  determine the filter options.

[default_filter(filters)]
  Takes a hash of (column, value) pairs, to default a filter to something
  other than 'All'.

[filter_options_for(column, choices, &block)]
  Specifies a fixed set of choices to be offered as filter options instead of
  automatically working it out. Choices should be a (value, label) hash. The
  optional block will be given each value in turn, and should return an SQL
  condition fragment.

[column_labels(labels)]
  Takes a hash of (column, label) pairs, to change the default label for a
  field to explicitly define the human label for a column. This label will be
  the default used in both list and edit views.

[list_columns(*columns, &proc)]
  Takes either a simple-list of column names, or a Field Definition Block (see
  next section) or both.

  Please, note that a list of column names will produce a bunch of fields
  using the +static_text+ helper almost for everything but the first column
  (which will get a special treatment to allow you to access the "edit view").
  If a column occurs more than once (summing up the list of column names and
  the FDB), then that column will appear more than once:

    list_columns :name, :code, :address do |f|
      f.hyperlink :address
    end

  The previous code fragment will list the +address+ column twice: the first
  one will be shown as static text, the second one will produce a real link.

  At the moment, if you have a long list of columns and want to customize only
  a few of them, you have to write then down one by one, even those that don't
  need special care.

[admin_fieldset(label='', *columns, &proc)]
  Defines a fieldset for edit views. For simple use, you can just give it a
  list of columns. Once you get started, you'll want to pass a Field
  Definition Block, though.

  In the Field Definition Block you can get hold of the model instance using
  the block's parameter; let's say the model has an attachment, handled by
  paperclip[http://github.com/thoughtbot/paperclip], called +picture+ and you
  want to show the image's URL like an hyperlink:

    admin_fieldset do |f|
      # ...
      f.hyperlink :picture, :url => f.object.picture.url
    end

  At the moment, this does not work with +list_columns+.

  Most of the helpers which accept or require a block will yield the model
  instance to the block, if it requires a parameter:

    admin_fieldset do |f|
      # ...
      f.static_image :picture do |cover|
        {:src => cover.picture.url}
      end
    end

  This works also with +list_columns+.

[admin_child_table(label, collection, options={}, &proc)]
  Defines a fieldset for edit views, to show a table of items from a child
  collection. It uses a Field Definition Block to declare what columns should
  be shown. Generally, you'd want to use the static_text helper, I suspect.
  *WARNING*: This has no tests, and I'm almost certain it will break horribly
  if you try to use anything other than static_text.

[admin_child_form(collection, options={}, &proc)]
  Defines a "fieldset" for edit views, to show *several* fieldsets, each
  containing one object from a child collection. It uses a Field Definition
  Block to declare what columns should be shown. I don't think it'd be wise to
  use this on a large collection, but it's your application. :) *WARNING*:
  This also has no tests, and I believe it will break horribly if you try to
  use it at all.


== Field Definition Block?!?

A number of the above methods provide for a block to declare what fields are
to be shown. This is achieved by yielding a builder to the block. Depending on
context, the mood of a theme author, and the phase of the moon, a given block
will see several builders in its lifetime. Not all builders will have an
active object; all will respond to the +object+ method, though. A basic field
definition block will just call a field helper on the builder for each field
that it wishes to display. The +auto_field+ helper (which automatically
determines an appropriate field type based on column and association metadata)
is available if you only want to specify the field type for some of the
fields. All field helpers take (field_name, options={},
*other_stuff). Most just take the two parameters, and I'm considering
deprecating the extra parameters on those that currently support them. Note
that unlike a standard builder, you don't have to do anything with the return
value; the theme's actual FormBuilder is wrapped by a DeclarativeFormBuilder,
which takes care of that for you.

In theory, there's no compelling reason you can't add complex logic to a field
definition block, such as examining the current user, or even the builder's
active object (though I strongly encourage you to handle nil permissively, at
this stage). It would be unwise to vary the fields returned based on the
object for a list view, for fairly obvious reasons.


== Available Form Helpers

* Simple helpers that just delegate to the ActionView's FormBuilder:
  +hidden_field+, +date_select+, +datetime_select+, +text_field+, +text_area+,
  +check_box+, +secure_password+, +file_field+.

* +select+ and +radio_group+ operate in basically the same way; they both
  provide a method of selecting one out of several choices (ignoring
  select :multiple, that is). Note that select's list of choices,
  normally the second parameter to the select helper, has been relegated to a
  :choices entry in the options, for API consistency.

* +static_text+ just outputs an HTML-escaped string representation of the
  field's value. It is useful both for read-only fields in forms, and as the
  primary helper in lists.

* +calculated_text+ requires a block to which it will yield the model
  instance; the block's product will be treated like a string and
  HTML-escaped.

* +static_html+ is like +static_text+, but the final product will not be
  HTML-escaped.

* +calculated_html+ is like +calculated_text+, but the final product will not
  be HTML-escaped.

* +auto_field+, as discussed above, will automatically select a suitable field
  helper, based on the column and association metadata. Where there are
  multiple suitable candidates, it tries to go for the more
  generally-applicable choice (for example, it favours a +select+ over a
  +radio_group+ for a +belongs_to+ association).

* +static_image+ sports a number of options used to build a hash suitable for
  the +tag+ helper responsible for the creation of final  tag:

  * :controller (default "auto_admin") and :action (default
    "edit") allows to select a controller which should return the image based
    upon this object's id;

  * :src override the previous two options and can be used with any
    URL, static or dynamic;

  * with :size one can write the width and height used to show the
    image (format: "XxY");

  * :alt, which defaults to this object's +to_label+.

  Everything else is passed as is to the +tag+ helper (so one could use
  :style or :class to alter the looks of ).
  This helper accepts a block, with a single parameter (this object), which
  can be used to return a hash to be merged into the options that are about to
  be passed to the tag helper.

* +text_field_with_auto_complete+ provides a simple text field with
  autocompletion delegating all the work to the homonymous ActionView's
  helper; there's a complication, however: there must be a separate controller
  which provides the completion data:

    b.text_field_with_auto_complete :name,
      :completion => { :url => { :controller => 'items',
                                 :action => 'auto_complete_for_item_name' }}

  For information on this controller, see Rails documentation. Note that if
  you designed your application in a REST-like way, you should tweak your
  resources' definitios or add a specific route for the new
  auto_complete_for_model_field.

* +hyperlink+ automatically generates a link to the "edit view" of its first
  argument (which must be one of the primary objects); alternatively you can
  use the :url option to generate a custom link:

    f.hyperlink :picture, :url => f.object.picture.url

  Anyway, the link caption will be the URL itself, unless you use the option
  :link_text as follow:

    f.hyperlink :picture, :url => f.object.picture.url,
      :link_text => 'The picture'

* +html_area+ use FCKEditor if the support has been explicitly turned on,
  otherwise it delegates to +text_area+; when using FCKEditor, you can pass a
  hash of options down to the editor:

    f.html_area :content, :toolbarSet => 'MyStyle', :height => '300px'

  See the FCKEditor plugin[http://rubyforge.org/projects/fckeditorp/]'s
  documentation.

* None of the following actually work, but they're defined, waiting for me to
  come back and write them. Presumably the image fields will delegate to
  file_column: +image_field+, +static_file+.


== How does it work? - Part II, Themes

The theme bundled with the plugin is named 'django'; all credit for its
excellent appearance goes to the Django project. I hope we can get a couple of
standard themes, but they won't be coming from me... experience shows that I
shouldn't try to make things look good. I believe I've successfully drawn
lines in all the right places for what is in the plugin's core, and what's in
a theme. I've already developed most of a second theme (which will not be
released) for my employer, so the infrastructure is mostly proven. A more
coherent HOWTO on creating themes (which can just be installed as seperate
Rails plugins, then selected in environment.rb) will be forthcoming Real Soon
Now, though this section has ended up covering most of the basics.

The 30 second summary -- a theme comprises:

* FormBuilder (subclass of AutoAdminSimpleTheme::FormBuilder), to create an
  Edit screen (a real form)

* TableBuilder (subclass of AutoAdmin::TableBuilder(FormBuilder)), to create a
  List screen (a creative interpretation of "form", which seems to map
  surprisingly well, for now).

* FormProcessor (subclass of AutoAdminSimpleTheme::FormProcessor), which
  implements the same set of helper methods as the FormBuilders, but instead
  of returning HTML, its job is to perform any transformations on the params
  hash to correspond with unusual form field representations -- the base
  FormProcessor transforms keys referencing associations to reference the
  underlying columns (actor -> actor_id), for example. This class will often
  be empty, especially once I provide a facility with which to inject custom
  field helpers (for composed_of and maybe some belongs_to, mostly) into the
  base builder and processor.

* A complete set of views, including a layout, which delegate the hard work to
  the FormBuilders.

* A 'public' directory, containing any required image, javascript, and
  stylesheet assets.

* A wrapper module, AutoAdmin#{name}Theme, which is responsible for:

  * Containing the FormBuilders and FormProcessor

  * Returning the full filesystem path to the 'views' and 'public' directories

  * Returing any theme-specific helpers, for injection into the controller

  * Injecting any theme-specific includes for ActiveRecord::Base (I've proven
    this to be possible, though can't think of a sane reason a theme would
    want to do so)

Extending your theme module with AutoAdmin::ThemeHelpers will help to keep the
module fairly DRY; it provides a +helper+ method, which can be given a list of
modules and/or a block, and directs the 'view_directory' and 'asset_root'
methods to a directory(*subdirs) singleton method, which you must define --
presumably using \_\_FILE\_\_.

NB: For good reasons that I can't remember right now, a couple of helper
methods have APIs that don't match the standard Rails FormBuilder, despite
matching names. The one that comes to mind is +select+ -- the choices have
been moved into the options hash, to keep all method signatures of the form
(field_name, options, *other_stuff).


== What's planned, but missing?

The ability for the application to inject custom field types into the base
FormBuilder and FormProcessor. The theme-specific versions of these classes
are available so that, for example, a theme can decide how a date_field should
be presented, and can correspondingly recover the values from multiple
inputs... they don't map as well to an application's requirement for a
'currency' field. Of course, there's nothing stopping an application
re-opening the classes and adding an appropriate helper method to each...
there's just a bit of undesirable complexity involved if you want auto_field
to detect and use it (which suggests to me that auto_field needs a bit of a
rethink).

A way for the application to reliably extend the AutoAdminController, and add
appropriate views somewhere, for those occasions when you have a couple of
screens that need to be hand-crafted, such as a statistics display, or a
particular edit screen that needs a specialised workflow. Note that if you
feel this constraint too much, you're probably pushing the plugin into a role
it doesn't fit.

Simple methods allowing an application to add navigation options, and perhaps
the ability to insert Components into the "dashboard" on the index page?

A top-level "menu", containing links to the primary object lists by default,
that a theme can permanently display.


== Longer-term architectural considerations?

After starting off defining the administration interfaces directly in the
models (as Django does), I was strongly considering moving them all into an
application-specific controller, that would subclass AutoAdminController. I
haven't gotten around to doing that, and am now quite intruiged by the
approach taken by Streamlined -- adding a new type of class. Any such move is
primarily aimed at solving a problem I'm not yet sufferring, though, so for
now it's just a topic to ponder.


== Contributing

If you want to help:

* fork the project[http://github.com/baldowl/auto_admin] on GitHub;
* work in a topic branch;
* write your additions/bug fixes;
* commit;
* send me a pull request for the topic branch.

If you have any issue, please post them on the {project's issue
list}[http://github.com/baldowl/auto_admin] on GitHub.

本源码包内暂不包含可直接显示的源代码文件,请下载源码包。