module RearSetup
Public Instance Methods
when rendering some model in a “remote” association pane, all columns of current model will be displayed.
‘assoc_columns` allow to set a list of “remotely” displayed columns.
# File lib/rear/setup/associations.rb, line 28 def assoc_columns *columns @__rear__assoc_columns = columns if columns.any? @__rear__assoc_columns end
sometimes you need to filter by some value that has too much options. For ex. you want to filter pages by author and there are about 1000 authors in db. displaying all authors within a single dropdown filter is kinda cumbersome. we need to somehow narrow them down. decorative filters allow to do this with easy. in our case, we do not display the authors until a letter selected.
@example
class Pages < E include Rear model PageModel decorative_filter :letter, :select do ('A'..'Z').to_a end filter :author_id, :select do if letter = filter?(:letter) # use here the name of decorative filter authors = {} AuthorModel.all(:name.like => "%#{letter}%").each |a| authors[a.id] = a.name end authors else {"" => "Select a letter please"} end end end
@note
decorative filters will not actually query the db, so you can name them as you want.
@note
decorative filters does not support custom comparison functions
# File lib/rear/setup/filters.rb, line 200 def decorative_filter *args, &proc html_attrs = args.last.is_a?(Hash) ? Hash[args.pop] : {} setup = {decorative?: true, cmp: FILTERS__DECORATIVE_CMP} filter *args << html_attrs.merge(setup), &proc end
set HTML attributes to be used on all columns only on editor pages. @note will override any attrs set via ‘html_attrs`
# File lib/rear/setup/columns.rb, line 105 def editor_attrs attrs = {} @__rear__editor_attrs = attrs if attrs.any? && @__rear__editor_attrs.nil? @__rear__editor_attrs || html_attrs end
add a filter.
by default a text filter will be rendered. to define filters of another type, pass desired type as a Symbol via second argument.
acceptable types:
- :string/:text - :select - :radio - :checkbox - :date - :datetime - :time - :boolean
@note comparison function
:text/:string filters will use :like comparison function by default: ... WHERE name LIKE '%VALUE%' ... :checkbox filters will use :in comparison function by default: ... WHERE column IN ('VALUE1', 'VALUE2') ... if you use a custom cmp function with a :checkbox filter, filter's column will be compared to each selected value: ... WHERE (column LIKE '%VALUE1%' OR column LIKE '%VALUE2%') ... any other types will use :eql comparison function by default: "... WHERE created_at = 'VALUE' ... to use a non-default comparison function, set it via :cmp option: `filter :name, :cmp => :eql` available comparison functions: - :eql # equal - :not # not equal - :gt # greater than - :gte # greater than or equal - :lt # less than - :lte # less than or equal - :like # - column LIKE '%VALUE%' - :unlike # - column NOT LIKE '%VALUE%' - :_like # match beginning of line - column LIKE 'VALUE%' - :_unlike # - column NOT LIKE 'VALUE%' - :like_ # match end of line - column LIKE '%VALUE' - :unlike_ # - column NOT LIKE '%VALUE' - :_like_ # exact match - column LIKE 'VALUE' - :_unlike_ # - column NOT LIKE 'VALUE'
@note if type not given,
Rear will use the type of the column with same name, if any. if no column found, it will use :text
@note :radio, :checkbox and :select filters requires a block to run.
block should return an Array or a Hash. use an Array when stored keys are the same as displayed values. use a Hash when stored keys are different. Important! if no block given, Rear will search for a column with same name and type and inherit options from there. so if you have say a :checkbox column named :colors with defined options, you only need to do `filter :colors`, without specifying type and options. type and options will be inherited from earlier defined column.
@example
class Page < ActiveRecord::Base # ... include Rear rear do # text filter using :like comparison function filter :name # text filter using :eql comparison function filter :name, :cmp => :eql # date filter using :eql comparison function filter :created_at, :date # date filter using :gte comparison function filter :created_at, :date, :cmp => :gte # dropdown filter using :eql comparison function filter :color, :select do ['Red', 'Green', 'Blue'] end # dropdown filter using :like comparison function filter :color, :select, :cmp => :like do ['Red', 'Green', 'Blue'] end end end
@example :radio filter using Hash
rear do filter :color, :radio do {'r' => 'Red', 'g' => 'Green', 'b' => 'Blue'} end end
@example inheriting type and options from a earlier defined column
rear do column :colors, :checkbox do options 'Red', 'Green', 'Blue' end filter :colors # type and options inherited from :colors column end
@param [Symbol] column @param [Symbol] type @param [Hash] opts_and_or_html_attrs @options opts_and_or_html_attrs :cmp comparison function @options opts_and_or_html_attrs :label @param [Proc] options block used on :select, :radio and :checkbox filters
should return Array or Hash.
# File lib/rear/setup/filters.rb, line 121 def filter column, type = nil, opts_and_or_html_attrs = {}, &proc opts = (opts_and_or_html_attrs||{}).dup type.is_a?(Hash) && (opts = type.dup) && (type = nil) && (opts_and_or_html_attrs = nil) matching_column = columns.find {|c| c && c.first == column} # if no type given, inheriting it from a column with same name, if any. type ||= (matching_column||[])[1] type = FILTERS__DEFAULT_TYPE unless FILTERS__HANDLED_TYPES.include?(type) # if filter is of :select, :radio or :checkbox type and no options block given, # inheriting it from a column with same name, if any. if proc.nil? && matching_column && matching_column[1] == type mci = RearInput.new(matching_column[0], type, &matching_column[3]) mci.optioned? && proc = lambda { mci.options } end # using defaults if no comparison function given unless cmp = opts.delete(:cmp) cmp = case type when :text, :string :like when :checkbox :in else :eql end end unless label = opts.delete(:label) label = column.to_s label << '?' if type == :boolean end (filters[column.to_sym] ||= {})[cmp] = { template: 'filters/%s.slim' % type, type: type, label: label.freeze, decorative?: opts.delete(:decorative?), attrs: opts.freeze, proc: proc }.freeze end
set HTML attributes to be used on all columns on both pane and editor pages
# File lib/rear/setup/columns.rb, line 91 def html_attrs attrs = {} @__rear__html_attrs = attrs if attrs.any? && @__rear__html_attrs.nil? @__rear__html_attrs || {} end
ignore some assocs. this is a cosmetic measure - assocs just wont be displayed on frontend but the API for their manipulation will still work
# File lib/rear/setup/associations.rb, line 15 def ignored_assocs *assocs (@__rear__ignored_assocs ||= []).concat(assocs) if assocs.any? (@__rear__ignored_assocs || []) end
add new column or override automatically added one
@param [Symbol] name @param [Symbol] type one of [:string, :text, :date, :time, :datetime, :boolean]
default: :string
@param [Hash] opts_and_or_html_attrs @option opts_and_or_html_attrs :pane
when set to false the column wont be displayed on pane pages
@option opts_and_or_html_attrs :editor
when set to false the column wont be displayed on editor pages
@option opts_and_or_html_attrs :label @option opts_and_or_html_attrs :readonly @option opts_and_or_html_attrs :disabled @option opts_and_or_html_attrs :multiple @option opts_and_or_html_attrs any attributes to be added to HTML tag
@example
input :name # => <input type="text" value="...">
@example
input :name, :style => "width: 100%;" # => <input style="width: 100%;" type="text" ...>
@example
input :name, :text, :cols => 40 # => <textarea cols="40" ...>...</textarea>
@example display author name only on pane pages
input(:author_id) { disable :editor }
@example Ace Editor
input :content, :ace
@example CKEditor
input :content, :ckeditor
# File lib/rear/setup/columns.rb, line 40 def input name, type = nil, opts_and_or_html_attrs = {}, &proc type.is_a?(Hash) && (opts_and_or_html_attrs = type) && (type = nil) opts_and_or_html_attrs[:row] = opts_and_or_html_attrs[:row] ? opts_and_or_html_attrs[:row].to_s : @__rear__row existing_column = nil columns.each_with_index {|c,i| c && c.first == name && existing_column = [c,i]} column = existing_column ? Array.new(existing_column.first) : [] column[0] = name column[1] = type ? type.to_s.downcase.to_sym : column[1] || COLUMNS__DEFAULT_TYPE column[2] = (column[2]||{}).merge(opts_and_or_html_attrs).freeze column[3] = proc column.freeze existing_column ? columns[existing_column.last] = column : columns << column end
# then we using internal_filter # to yield selected category and filter articles internal_filter do if category_id = filter?(:Category) Article.all(category_id: category_id.to_i) end end
end
# File lib/rear/setup/filters.rb, line 304 def internal_filter &proc # instance_exec at runtime is expensive enough, # so compiling procs into methods at load time. chunks = [self.to_s, proc.__id__] name = ('__rear__%s__' % chunks.join('_').gsub(/\W/, '_')).to_sym define_method name, &proc private name internal_filters.push(name) end
# File lib/rear/setup/generic.rb, line 33 def items_per_page n = nil @__rear__ipp = n.to_i if n @__rear__ipp || 10 end
tell controller to create a CRUD interface for given model opts and proc will be passed to Espresso’s ‘crudify` helper.
@param [Class] model @param [Hash] opts to be passed to ‘crudify` method @param [Proc] proc to be passed to `crudify` method
# File lib/rear/setup/generic.rb, line 10 def model model = nil, opts = {}, &proc return @__rear__model if @__rear__model || model.nil? model = RearUtils.extract_constant(model) RearUtils.is_orm?(model) || raise(ArgumentError, '"%s" is not a ActiveRecord/DataMapper/Sequel model' % model.inspect) @__rear__model = model @__rear__default_label = model.name.gsub(/\W/, '_').freeze RearControllerSetup.crudify self, model, opts, &proc end
# File lib/rear/setup/generic.rb, line 50 def on_delete &proc before :destroy, &proc end
executed when new item created and when existing item updated
# File lib/rear/setup/generic.rb, line 40 def on_save &proc # const_get(:RearController). before :save, &proc end
executed when existing item updated
# File lib/rear/setup/generic.rb, line 46 def on_update &proc before :update, &proc end
# File lib/rear/setup/generic.rb, line 28 def order_by *columns @__rear__order = columns if columns.any? @__rear__order end
set HTML attributes to be used on all columns only on pane pages. @note will override any attrs set via ‘html_attrs`
# File lib/rear/setup/columns.rb, line 98 def pane_attrs attrs = {} @__rear__pane_attrs = attrs if attrs.any? && @__rear__pane_attrs.nil? @__rear__pane_attrs || html_attrs end
# File lib/rear/setup/generic.rb, line 20 def pkey key = nil return unless model @__rear__pkey = key if key @__rear__pkey || raise(ArgumentError, "Was unable to automatically detect primary key for %s model. Please set it manually via `pkey key_name`" % model) end
@example Array with default comparison function
quick_filter :color, 'Red', 'Green', 'Blue' ... WHERE color = '[Red|Green|Blue]'
@example Array with custom comparison function
quick_filter :color, 'Red', 'Green', 'Blue', :cmp => :like ... WHERE color LIKE '[Red|Green|Blue]'
@example Hash with default comparison function
quick_filter :color, 'r' => 'Red', 'g' => 'Green', 'b' => 'Blue' ... WHERE color = '[r|g|b]'
@example Hash with custom comparison function
quick_filter :color, :cmp => :like, 'r' => 'Red', 'g' => 'Green', 'b' => 'Blue' ... WHERE color LIKE '%[r|g|b]%'
@example Hash with comparison function defined per filter
quick_filter :color, [:like, 'r'] => 'Red', 'g' => 'Green', 'b' => 'Blue' on Red ... WHERE color LIKE '%r%' on Green or Blue ... WHERE color = '[g|b]'
# File lib/rear/setup/filters.rb, line 229 def quick_filter column, *args options = args.last.is_a?(Hash) ? args.pop : {} cmp = options.delete(:cmp) || :eql query_formats = FILTERS__QUERY_MAP.call(orm) if query_format = query_formats[cmp] options = Hash[options.map do |k,v| [ v.to_s, k.is_a?(Array) ? [query_formats[k.first], k.last] : [query_format, k] ] end] # if options provided as arguments, adding them to options Hash args.each {|a| options[a.to_s] = [query_format, a.to_s] } # if no options given, # inheriting them from a column with same name, if any. if options.empty? && mc = columns.find {|c| c && c.first == column} mci = RearInput.new(mc[0], mc[1], &mc[3]) mci.optioned? && mci.options.each_pair do |k,v| options[v.to_s] = [query_format, k] end end quick_filters[column.to_sym] = options end end
# File lib/rear/setup/generic.rb, line 55 def readonly! @__rear__readonly = true end
make some assocs readonly. this is a cosmetic measure - frontend just wont let user modify them but the API for their manipulation will still work
# File lib/rear/setup/associations.rb, line 6 def readonly_assocs *assocs (@__rear__readonly_assocs ||= []).concat(assocs) if assocs.any? (@__rear__readonly_assocs || []) end
reset any automatically(or manually) added columns
# File lib/rear/setup/columns.rb, line 62 def reset_columns! @__rear__columns = {} end
display multiple columns in a row(on editor)
@example using a block
row :Location do column :country column :state column :city end
@example without a block
column :country, :row => :Location column :state, :row => :Location column :city, :row => :Location
# File lib/rear/setup/columns.rb, line 82 def row label = nil, &proc # explicit labels will be strings and implicit ones will be numbers # as a way to distinguish them when rendering templates @__rear__row = label ? label.to_s : (Time.now.to_f + rand) self.instance_exec(&proc) if proc @__rear__row = nil end