Listings
By Manas
Listings aims to simplify the creations of listings for Rails 3 & 4 apps.
The listings created support when approriate sorting, pagination, scoping, searching, filtering and exports to csv or xls.
A listing data source have built in support for ActiveRecord and Arrays.
- Listings
- Download
- Configuration
- Usage
- Samples
- Listings DSL
- model
- column
- scope
- filter
- paginates_per
- export
- css
- Testing
- i18n
- Templates
- Javascript api
- Under the hood
- License
Download
Add listings to your Gemfile:
gem 'listings'Configuration
Mount listings engine to in you config/routes.rb
Rails.application.routes.draw do
...
mount Listings::Engine => "/listings"
endRequire assets in your Javascript and Stylesheet
//= require listings
*= require listings
Create config/initializers/listings.rb file to make general configurations. Listings plays nice with twitter bootstrap 2 & 3 without explicitly including it.
# file: config/initializers/listings.rb
Listings.configure do |config|
config.theme = 'twitter-bootstrap-3' # defaults to 'twitter-bootstrap-2'
config.push_url = true # User html5 history push_state to allow back/forward navigation. defaults to false
endUsage
Create listings files app/listings/{{name}}_listing.rb. See listings samples and the DSL
Use render_listing helper to include the listing by its name.
# file: app/listings/tracks_listing.rb
class TracksListing < Listings::Base
...
end= render_listing :tracks
Samples
For a Track/Album model:
class Track < ActiveRecord::Base
belongs_to :album
attr_accessible :order, :title
scope :favorites, -> { ... }
end
class Album < ActiveRecord::Base
attr_accessible :name
has_many :tracks
enda listing with
class TracksListing < Listings::Base
model Track
scope :all
scope :favorites
filter album: :name
column :order
column :title, searchable: true
column album: :name, searchable: true
endListings DSL
A listing inherits from Listings::Base and defines de following DSL
model
model can be used with just an ActiveRecord class
model Trackor with a block to perform further operations
model do
Track.favorites
endor generate an array of objects or hashes
model do
[
{title: "Tishomingo Blues", album: {name: "The Royal J's" }},
{title: "Save me for later", album: {name: "Me 'n' Mabel" }}
]
endcolumn
Declaring a column with a symbol renders that attribute
column :titlecolumn accepts options. By default { searchable: false, sortable: true }
Adding searchable: true will make a search box appear and perform a search depending on the datasource logic (field LIKE '%pattern%' for ActiveRecord datasource or include? for Object datasource)
column :title, searchable: truecolumn can traverse object path and belongs_to relations
column album: :nameOr
column [:album, :name]column also accepts a block to alter the rendering of the field
column album: :name do |track, album_name|
album_name.titleize
endThe block is evaluated in a view_context so any helper you will usually use in a view it can be used in the block
column do |track|
link_to 'Edit', edit_track_path(track)
end
column do |track|
# renders app/views/shared/_tracks_actions view
render partial: 'shared/track_actions', locals: {track: track}
endcolumn also accepts a title: option
column album: :name, title: 'Album'computed columns
Listings will try to sort and filter by default using the
<table_name>.<column_name>field of the query, on computed queries this will not work, you will need to usecolumn :my_computed_field, query_column: :my_computed_fieldto allow sorting and filtering.Using computed columns might cause some issues depending on the Kaminari version used. If this is the case and you need to filter based on a computed filed, you will need to define a
custom_filterto apply the filtering of the computed field.See example at spec/dummy/app/listings/authors_listing.rb
scope
Declaring a scope with a symbol with allow user to show only records matching the scope in the ActiveRecord class
scope :favoritesYou can change the displayed name but yet, using the declared scope
scope 'My favorites', :favoritesIf you don't want to pollute the model with scopes, or you need to filter items with a custome logic use a block
scope 'My favorites', :favorites, lambda { |items| items.where(...) }filter
Declaring a filter will display a unique list of values of the field and allow the user to filter on exact match
filter album: :nameIt supports title: and a block
filter album: :name, title: 'Album' do |album_name|
album_name.titleize
endA values: :method can be specified to avoid the select distinct over the values.
filter album: :name, values: :all_album_names, title: 'Album' do |album_name|
album_name.titleize
end
def all_album_names
Album.order("name").pluck("distinct name").reject(&:nil?)
endAlso render: option can be used to suppress the rendering of the filter, but allowing the user to filter by it. For example to filter by the id:
filter :id, render: falseOr render: can be user to indicate the partial to be used for rendering that filter. Hence allowing custom UI for certain filter among the default UI. See Templates for information regarding where the partial will be looked.
filter :update_at, render: 'date'Filters are rendered by default by the side of the listing. With layout method you can change this and render them on the top.
layout filters: :topCustom filters allows definition of custom meaning to a key. This filters are not rendered by default. Use render: option to indicate the partial to be used for rendering.
custom_filter :order_lte do |items, value|
items.where('"order" <= ?', value.to_i)
endpaginates_per
Page size can be specified by paginates_per
paginates_per 40And you can disable pagination
paginates_per :noneexport
Declare which formats you want to be available for export. Notice that paging will be ignored for the export.
export :csv, :xlsSometimes the columns should be rendered different for the export a format property is available and will be :html, :csv or :xls.
column :email do |user, email|
if format == :html
mail_to email
else
email
end
endcss
css_class ca specify a css class to be apply to the listing
css_class 'my-custom-style'row_style can specigy a css class to apply to each row depending on the item it is rendering
row_style do |track|
'favorite-track' if track.favorite?
endA column also support a class option to specify a css class to be applied on every table cell
column :title, class: 'title-style'Testing
Include listings/rspec in your spec_helper.rb
# file: spec/spec_helper.rb
require 'listings/rspec'Ensure listing is able to render
# file: spec/listings/tracks_listing_spec.rb
require 'spec_helper'
RSpec.describe TracksListing, type: :listing do
let(:listing) { query_listing :tracks }
it 'should get tracks' do
# ... data setup ...
items = listing.items.to_a
# ... assert expected items ...
end
endi18n
Although titles can be specified in the listing definition, i18n support is available. Add to your locales:
es:
listings:
no_data: "No se encontraron %{kind}"
export: "Descargar"
search_placeholder: "Buscar %{kind}"
records: "registros"
headers:
tracks:
title: 'Título'
album_name: 'Nombre del Album'Javascript api
The listing content can be reloaded preserving current sort, scope, search, filter and page.
To reload the listings rendered by
= render_listing :tracks
use
refreshListing('tracks')Change filters
$('#tracks.listings').trigger("listings:filter:key:clear", 'album_name')
$('#tracks.listings').trigger("listings:filter:key:set", ['album_name', 'Best of'])
View Helpers
Use render_listing helper to include the listing by its name.
= render_listing :tracks
Use listings_link_to_filter helper to render a link that will set a filter. Used to example when you want the user to be able to click on a cell value to filter upon that.
column artist: :name do |album, value|
listings_link_to_filter(value, :artist_id, album.artist_id)
endTemplates
There are a number of templates involved in rendering the listing. These templates can be rendered by the hosting app per listing or theme basis.
For example if a listing named tracks is rendered with twitter-bootstrap-3 theme the templates are looked up in the following locations/order:
<app>/views/listings/tracks/<partial><gem>/views/listings/tracks/<partial><app>/views/listings/twitter-bootstrap-3/<partial><gem>/views/listings/twitter-bootstrap-3/<partial><app>/views/listings/<partial><gem>/views/listings/<partial>
This lookup logic is inside Listings::ActionViewExtensions#listings_partial_render.
Under the hood
The first listing is rendered in the context of the request, so the first page will be displayed with no delay.
Any further interaction with the listing will end up in a AJAX call attended by the mountable engine.
License
listings is Copyright © 2003 Manas Technology Solutions. It is free software, and may be redistributed under the terms specified in the LICENSE file.