README.md in admino-0.0.5 vs README.md in admino-0.0.6

- old
+ new

@@ -45,11 +45,11 @@ ```ruby class TasksQuery < Admino::Query::Base end ``` -Each query object gets initialized with a hash of params, and features a `#scope` method that returns the filtered/sorted result set. As you may have guessed, query objects can be great companions to index controller actions: +Each query object gets initialized with a hash of params, and features a `#scope` method that returns the filtered/sorted result set. As you may have guessed, query objects can be great companions to index actions: ```ruby class TasksController < ApplicationController def index @query = TasksQuery.new(params) @@ -84,12 +84,11 @@ class TasksQuery < Admino::Query::Base # ... search_field :title_matches end ``` -The `#scope` method will check the presence of the `params[:query][:title_matches]` key. If it finds it, it will augment the query with a -named scope called `:title_matches`, expected to be found within the `Task` model, that needs to accept an argument. +The `#scope` method will check the presence of the `params[:query][:title_matches]` key. If it finds it, it will augment the query with a named scope called `:title_matches`, expected to be found within the `Task` model. The scope needs to accept an argument. ```ruby class Task < ActiveRecord::Base scope :title_matches, ->(text) { where('title ILIKE ?', "%#{text}%") @@ -110,11 +109,11 @@ # ... filter_by :status, [:completed, :pending] end ``` -Just like a search field, with a declared filter group the `#scope` method will check the presence of a `params[:query][:status]` key. If it finds it (and its value corresponds to one of the declared scopes) it will augment the query the scope itself: +Just like a search field, with a declared filter group the `#scope` method will check the presence of a `params[:query][:status]` key. If it finds it (and its value corresponds to one of the declared scopes) it will augment the query with the scope itself: ```ruby class Task < ActiveRecord::Base scope :completed, -> { where(completed: true) } scope :pending, -> { where(completed: false) } @@ -248,37 +247,26 @@ ### Simple Form support The presenter also offers a `#simple_form` method to make it work with [Simple Form](https://github.com/plataformatec/simple_form) out of the box. -### I18n +### Output customization -To localize the search form labels, as well as the group filter names and scope links, please refer to the following YAML file: +The `#scope_link` methods are very flexible, allowing you to change almost every aspect of the generated links: -```yaml -en: - query: - attributes: - tasks_query: - title_matches: 'Title contains' - filter_groups: - tasks_query: - status: - name: 'Filter by status' - scopes: - completed: 'Completed' - pending: 'Pending' - sorting_scopes: - task_query: - by_due_date: 'By due date' - by_title: 'By title' +```erb +<% status_filter = query.filter_group_by_name(:status) %> + +<%= status_filter.scope_link :completed, + 'Custom title', + active_class: 'active', + class: 'custom-class' +%> ``` -### Output customization +Please refer to the tests for the details. -The presenter supports a number of optional arguments that allow a great amount of flexibility regarding customization of CSS classes, labels and HTML attributes. Please refer to the tests for the details. - ### Overwriting the starting scope Suppose you have to filter the tasks based on the `@current_user` work group. You can easily provide an alternative starting scope from the controller passing it as an argument to the `#scope` method: ```ruby @@ -288,48 +276,79 @@ end ``` ### Coertions -Admino can perform automatic coertions from a param string input to the type needed by the model named scope: +Suppose the presence of a model scope that requires a non-textual argument (ie. a date): ```ruby +class Task < ActiveRecord::Base + scope :due_date_from, ->(date) { where('due_date >= ?', date) } +end +``` + +Admino can perform some automatic coertions to the textual parameter it gets, and pass the coerced value to the scope: + +```ruby class TasksQuery < Admino::Query::Base - # ... - field :due_date_from, coerce: :to_date - field :due_date_to, coerce: :to_date + search_field :due_date_from, coerce: :to_date end + +query = TaskQuery.new(query: { due_date_from: '2014-03-01' }) +query.search_field_by_name(:due_date_from).value # => #<Date Sat, 01 Mar 2014> ``` -The following coertions are available: +If a specific coercion cannot be performed with the provided input, the scope won't be chained. The following coertions are available: + * `:to_boolean` * `:to_constant` * `:to_date` * `:to_datetime` * `:to_decimal` * `:to_float` * `:to_integer` * `:to_symbol` * `:to_time` -If a specific coercion cannot be performed with the provided input, the scope won't be chained. - Please see the [`Coercible::Coercer::String`](https://github.com/solnic/coercible/blob/master/lib/coercible/coercer/string.rb) class for details. ### Default sorting -If you need to setup a default sorting, you can pass some optional arguments to a `scoping` declaration: +If you need to setup a default sorting, you can pass some optional arguments to the `sorting` declaration: ```ruby class TasksQuery < Admino::Query::Base # ... sorting :by_due_date, :by_title, default_scope: :by_due_date, default_direction: :desc end ``` +### I18n + +To localize the search form labels, as well as the group filter names and scope links, please refer to the following YAML file: + +```yaml +en: + query: + attributes: + tasks_query: + title_matches: 'Title contains' + filter_groups: + tasks_query: + status: + name: 'Filter by status' + scopes: + completed: 'Completed' + pending: 'Pending' + sorting_scopes: + task_query: + by_due_date: 'By due date' + by_title: 'By title' +``` + ## Admino::Table::Presenter Admino offers a [Showcase collection presenter](https://github.com/stefanoverna/showcase) that makes it really easy to generate HTML tables from a set of records: ```erb @@ -346,29 +365,29 @@ <table> <thead> <tr> <th role='title'>Title</th> <th role='completed'>Completed</th> - <th role='due_date'>Due date</th> + <th role='due-date'>Due date</th> </tr> <thead> <tbody> <tr id='task_1' class='is-even'> <td role='title'>Call mum ASAP</td> <td role='completed'>✓</td> - <td role='due_date'>2013-02-04</td> + <td role='due-date'>2013-02-04</td> </tr> <tr id='task_2' class='is-odd'> <!-- ... --> </tr> <tbody> </table> ``` ### Record actions -Often table rows needs to offer some kind of action associated with the record. The presenter implements the following DSL to support that: +Often tables need to offer some kind of action associated with the records. The presenter implements the following DSL to support that: ```erb <%= Admino::Table::Presenter.new(@tasks, Task, self).to_html do |row, record| %> <%# ... %> <%= row.actions do %> @@ -400,12 +419,20 @@ </table> ``` ### Sortable columns -Once a query object is passed to the presenter, columns can be associated to specific sorting scopes of the query object using the `sorting` option: +If you want to make the table headers sortable, then please create an Admino query object class to define the available sorting scopes. +```ruby +class TaskQuery < Admino::Query::Base + sorting :by_title, :by_due_date +end +``` + +You can then pass the query object as a parameter to the table presenter initializer, and associate table columns to specific sorting scopes of the query object using the `sorting` directive: + ```erb <% query = present(@query) %> <%= Admino::Table::Presenter.new(@tasks, Task, query, self).to_html do |row, record| %> <%= row.column :title, sorting: :by_title %> @@ -418,24 +445,24 @@ ```html <table> <thead> <tr> <th role='title'> - <a href="/admin/tasks?sorting=by_title&sort_order=desc" class='is-asc'>Title</a> + <a href='/admin/tasks?sorting=by_title&sort_order=desc' class='is-asc'>Title</a> </th> - <th role='due_date'> - <a href="/admin/tasks?sorting=by_due_date&sort_order=asc" class='is-asc'>Due date</a> + <th role='due-date'> + <a href='/admin/tasks?sorting=by_due_date&sort_order=asc'>Due date</a> </th> </tr> <thead> <!-- ... --> </table> ``` ### Customizing the output -The `#column` and `#action` methods are very flexible, allowing youto change almost every aspect of the generated table cells: +The `#column` and `#action` methods are very flexible, allowing you to change almost every aspect of the generated table cells: ```erb <%= Admino::Table::Presenter.new(@tasks, Task, self).to_html(class: 'table-class') do |row, record| %> <%= row.column :title, 'Custom title', class: 'custom-class', role: 'custom-role', data: { custom: 'true' }, @@ -445,11 +472,11 @@ class: 'custom-class', role: 'custom-role', data: { custom: 'true' } %> <% end %> ``` -If you need more power, you can also decide to subclass `Admino::Table::Presenter`. For each HTML element, there's a set of methods you can override to customize it's appeareance. +If you need more power, you can also subclass `Admino::Table::Presenter`. For each HTML element, there's a set of methods you can override to customize it's appeareance. Table cells are generated through two collaborator classes: `Admino::Table::HeadRow` and `Admino::Table::ResourceRow`. You can easily replace them with a subclass if you want. To grasp the idea here's an example: ```ruby class CustomTablePresenter < Admino::Table::Presenter private @@ -498,21 +525,15 @@ end ``` Please refer to the tests for all the details. -### Inherited resources +### Inherited resources (and similar) -If the action URLs can be programmatically generated, it becomes even easier to specify the table actions: +If your controller actions are generated through [Inherited Resources](https://github.com/josevalim/inherited_resources), then you can always get the URL pointing to the show action with the `resource_path` helper method. Similar helpers [are available for the other REST actions too](https://github.com/josevalim/inherited_resources#url-helpers) (new, edit, destroy). -```erb -<%= CustomTablePresenter.new(@tasks, Task, self).to_html do |row, record| %> - <%# ... %> - <%= row.actions :show, :edit, :destroy %> -<% end %> -``` -For instance, using [Inherited Resources](https://github.com/josevalim/inherited_resources) to generate controller actions, you can use its [helper methods](https://github.com/josevalim/inherited_resources#url-helpers) to build a custom subclass of `Admino::Table::Presenter`: +More in general, if you are able to programmatically generate/obtain the URLs of your row actions, you can subclass `Admino::Table::Presenter` and declare them: ```ruby class CustomTablePresenter < Admino::Table::Presenter private @@ -536,9 +557,18 @@ def destroy_action_html_options { method: :delete } end end end +``` + +This will enable you to generate row actions even faster, simply declaring them as arguments to the `#actions` DSL method: + +```erb +<%= CustomTablePresenter.new(@tasks, Task, self).to_html do |row, record| %> + <%# ... %> + <%= row.actions :show, :edit, :destroy %> +<% end %> ``` ### I18n Column titles are generated using the model [`#human_attribute_name`](http://apidock.com/rails/ActiveRecord/Base/human_attribute_name/class) method, so if you already translated the model attribute names, you're good to go. To translate actions, please refer to the following YAML file: