Module: RearHelpers::InstanceMixin

Includes:
RearConstants
Defined in:
lib/rear/helpers.rb,
lib/rear/helpers/order.rb,
lib/rear/helpers/pager.rb,
lib/rear/helpers/render.rb,
lib/rear/helpers/columns.rb,
lib/rear/helpers/filters.rb,
lib/rear/helpers/generic.rb

Constant Summary

Constants included from RearConstants

RearConstants::ASSETS__PATH, RearConstants::ASSETS__SUFFIX, RearConstants::ASSETS__SUFFIX_REGEXP, RearConstants::ASSOCS__STRUCT, RearConstants::COLUMNS__BOOLEAN_MAP, RearConstants::COLUMNS__DEFAULT_TYPE, RearConstants::COLUMNS__HANDLED_TYPES, RearConstants::COLUMNS__PANE_MAX_LENGTH, RearConstants::FILTERS__DECORATIVE_CMP, RearConstants::FILTERS__DEFAULT_TYPE, RearConstants::FILTERS__HANDLED_TYPES, RearConstants::FILTERS__QUERY_MAP, RearConstants::FILTERS__STR_TO_BOOLEAN, RearConstants::PAGER__SIDE_PAGES, RearConstants::PATH__TEMPLATES

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#columnObject

Returns the value of attribute column.



5
6
7
# File 'lib/rear/helpers/generic.rb', line 5

def column
  @column
end

#itemObject

Returns the value of attribute item.



5
6
7
# File 'lib/rear/helpers/generic.rb', line 5

def item
  @item
end

#item_idObject

Returns the value of attribute item_id.



5
6
7
# File 'lib/rear/helpers/generic.rb', line 5

def item_id
  @item_id
end

#itemsObject (readonly)

Returns the value of attribute items.



4
5
6
# File 'lib/rear/helpers/generic.rb', line 4

def items
  @items
end

Instance Method Details

#__rear__Object



64
65
66
# File 'lib/rear/helpers/generic.rb', line 64

def __rear__
  self.class
end

#associated_model_controller(model) ⇒ Object



59
60
61
62
# File 'lib/rear/helpers/generic.rb', line 59

def associated_model_controller model
  (@__rear__associated_model_controllers ||= {})[model] ||=
    RearUtils.associated_model_controller(model, :ensure_mounted)
end

#assocs(*types) ⇒ Object



28
29
30
31
32
33
# File 'lib/rear/helpers/generic.rb', line 28

def assocs *types
  return __rear__.assocs if types.empty?
  types.inject({}) do |assocs,type|
    assocs.merge(__rear__.assocs[type.to_sym] || {})
  end
end

#attrs(column, scope) ⇒ Object



30
31
32
33
34
35
# File 'lib/rear/helpers/columns.rb', line 30

def attrs column, scope
  meth = '%s_attrs' % scope
  column_attrs = column.send(meth)
  return column_attrs if column_attrs.any?
  __rear__.send(meth)
end

#columnsObject



24
25
26
27
28
# File 'lib/rear/helpers/columns.rb', line 24

def columns
  @__rear__columns ||= __rear__.columns.inject([]) do |columns,(name, type, attrs, proc)|
    columns << RearInput.new(name, type, attrs, @brand_new_item, &proc)
  end
end

#dom_idObject



11
12
13
14
# File 'lib/rear/helpers/generic.rb', line 11

def dom_id
  @__rear__dom_id ||= (params[:dom_id] || # params[:dom_id] are used by decorative filters
    (@reverse_assoc ? @reverse_assoc.dom_id : 'rear_element_%s' % self.__id__)).freeze
end

#editor_columnsObject



20
21
22
# File 'lib/rear/helpers/columns.rb', line 20

def editor_columns
  @__rear__editor_columns ||= columns.select {|c| c.editor?}
end

#filter(name, comparison_function = FILTERS__DECORATIVE_CMP) ⇒ Object Also known as: filter?, decorative_filter?



53
54
55
# File 'lib/rear/helpers/filters.rb', line 53

def filter name, comparison_function = FILTERS__DECORATIVE_CMP
  ((opted_filters[name] || {})[comparison_function] || []).first
end

#filter_setup_to_options(setup) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/rear/helpers/filters.rb', line 132

def filter_setup_to_options(setup)
  options = setup[:proc] ? self.instance_exec(&setup[:proc]) : {}
  if options.is_a?(Array)
    options.flatten!
    options = Hash[options.zip(options)]
  end
  return options if options.is_a?(Hash)
  warn "
  %s#%s filter expects options to be provided as a Hash or Array or via a block.
  If block given, it should return a Hash or Array
  " % [self.class, setup[:label]]
  {}
end

#filtersObject



4
5
6
# File 'lib/rear/helpers/filters.rb', line 4

def filters
  __rear__.filters
end

#filters_query_mapObject



49
50
51
# File 'lib/rear/helpers/filters.rb', line 49

def filters_query_map
  @@__rear__filters__query_map ||= FILTERS__QUERY_MAP.call(__rear__.orm)
end

#filters_to_sqlObject



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/rear/helpers/filters.rb', line 59

def filters_to_sql
  conditions, sql_chunks, values = {}, [], []
  
  quick_filters.each_pair do |column,filters|
    next unless label = opted_quick_filters[column]
    filters.each_pair do |filter,(query_formats,value)|
      next unless label == filter
      sql_chunks  << query_formats.first % RearUtils.quote_column(model, column)
      values << ([TrueClass, FalseClass, Regexp].include?(value.class) ? value : query_formats.last % value)
    end
  end

  opted_filters.each_pair do |column, setups|
    setups.each_pair do |cmp, (value, query_formats, filter_setup)|
      next if filter_setup[:decorative?]
      
      sql_chunk = query_formats.first % RearUtils.quote_column(model, column)
      custom_sql_chunk  = nil

      if filter_setup[:type] == :boolean && FILTERS__STR_TO_BOOLEAN.has_key?(value)
        values << FILTERS__STR_TO_BOOLEAN[value]
      else
        case cmp
        when :in
          values << value
        when :csl # comma separated list
          values << value.to_s.split(',')
        else
          if value.is_a?(Array)
            custom_sql_chunk = []
            value.each do |v|
              custom_sql_chunk << sql_chunk
              values << query_formats.last % v
            end
            custom_sql_chunk = '(' + custom_sql_chunk.join(' OR ') + ')'
          else
            values << query_formats.last % value
          end
        end
      end
      sql_chunks << (custom_sql_chunk || sql_chunk)
    end
  end

  __rear__.internal_filters.each do |m|
    next unless items = self.send(m)
    sql_chunks << filters_query_map[:in].first % pkey
    values     << items.map {|i| i[pkey]}
  end

  sql_chunks.any? ?
    conditions.merge(conditions: [sql_chunks.join(' AND '), *values]) :
    conditions
end

#modelObject



16
17
18
# File 'lib/rear/helpers/generic.rb', line 16

def model
  __rear__.model
end

#opted_filtersObject

turn => {‘gte’ => ‘foo’, ‘lte’ => ‘bar’} into {

:date => {
          :gte => ['foo', ['%s >= ?', '%s'] ],
          :lte => ['bar', ['%s <= ?', '%s'] ],
         }

}



34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/rear/helpers/filters.rb', line 34

def opted_filters
  @__rear__opted_filters ||= (params[:filters]||{}).inject({}) do |map,s|
    (column = s.first) && (filter = filters[(column = column.to_sym)]).is_a?(Hash) &&
      (setup = s.last).is_a?(Hash) && setup.each_pair do |cmp,v|
        v && v.size > 0 && (query_formats = filters_query_map[(cmp=cmp.to_sym)]) &&
          (filter_setup = filter[cmp]) && (map[column] ||= {})[cmp] = [
            v, # do not typecast to Fixnum cause this will break Hash to query_string conversion
            query_formats,
            filter_setup,
          ]
      end
    map
  end
end

#opted_quick_filtersObject



12
13
14
15
16
17
18
19
# File 'lib/rear/helpers/filters.rb', line 12

def opted_quick_filters
  @__rear__opted_quick_filters ||= begin
    given_filters = params[:quick_filters] || {}
    quick_filters.inject({}) do |map,(column,*)|
      (v = given_filters[column.to_s]) && v.size > 0 ? map.merge(column => v) : map
    end
  end
end

#options(*args) ⇒ Object

used for cosmetic compatibility between filters and columns. columns block are executed inside Column instance, and using ‘options` method to define options for :select/:radio/:checkbox columns. filters of these types also uses a block and it is executed in controller’s context. so adding this method here will allow to use ‘options` method inside filter’s block.

Examples:

filter :colors do
  options 'Red', 'Green', 'Blue'
end
# this is equivalent to 
filter :colors do
  ['Red', 'Green', 'Blue']
end
# but looks cosmetically better cause uses same syntax as columns


55
56
57
# File 'lib/rear/helpers/generic.rb', line 55

def options *args
  args
end

#order_paramsObject



25
26
27
28
29
30
# File 'lib/rear/helpers/order.rb', line 25

def order_params
  @__rear__order_params ||= pane_columns.inject({}) do |map,column|
    (vector = sortable_vector?(column)) ?
      map.update(column.string_name => vector) : map
  end.freeze
end

#order_params_to_sqlObject



32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/rear/helpers/order.rb', line 32

def order_params_to_sql
  order = []
  order_params.each_pair do |column_name, vector|
    next unless column = columns.find {|c| c.string_name == column_name}
    case __rear__.orm
    when :dm
      column.order_by.each {|c| order << c.send(vector)}
    when :ar
      columns = column.order_by.map {|c| RearUtils.quote_ar_column(model, c)}
      order  << [columns.join(', '), vector].join(' ')
    end
  end
  order.any? ? order : nil
end

#ormObject



7
8
9
# File 'lib/rear/helpers/generic.rb', line 7

def orm
  @__rear__orm ||= RearORM.new(__rear__.model, __rear__.pkey)
end

#pager_filtersObject



24
25
26
27
28
29
30
31
32
# File 'lib/rear/helpers/pager.rb', line 24

def pager_filters
  @__rear__pager_filters ||= filters.inject({}) do |map,(column, setups)|
    setups.each do |comparison_function, setup|
      (value = filter?(column, comparison_function)) &&
        (map[column] ||= {})[comparison_function] = value
    end
    map
  end
end

#pager_linker(page, label = nil) ⇒ Object



4
5
6
7
8
9
10
11
# File 'lib/rear/helpers/pager.rb', line 4

def pager_linker page, label = nil
  label ||= page
  url = route(action, *action_params__array, pager_params(page))
  return link_to(url, label) unless xhr?
  
  onclick = "Rear.switch_page('#%s', '%s');" % [dom_id, url]
  link_to nil, label, onclick: onclick
end

#pager_params(page = nil, filters = nil) ⇒ Object



13
14
15
16
17
18
19
20
21
22
# File 'lib/rear/helpers/pager.rb', line 13

def pager_params page = nil, filters = nil
  page.is_a?(Hash) && (filters = page) && (page = nil)
  page ||= params[:page]
  {
             page: page.to_s,
          filters: filters || pager_filters,
    quick_filters: opted_quick_filters,
            order: order_params
  }.reject {|k,v| v.nil? || v.empty?}
end

#pane_columnsObject



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/rear/helpers/columns.rb', line 4

def pane_columns
  @__rear__pane_columns ||= begin
    pane_columns = columns.select {|c| c.pane?}
    [RearInput.new(pkey)] + case action_name
    when :reverse_assoc
      (assoc_columns = __rear__.assoc_columns) ?
        pane_columns.select {|c| assoc_columns.include? c.name} :
        pane_columns[0..1]
    when :quickview
      pane_columns[0..0]
    else
      pane_columns
    end
  end
end

#path_to_rear_templates(*chunks) ⇒ Object

Raises:

  • (ArgumentError)


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/rear/helpers/render.rb', line 18

def path_to_rear_templates *chunks
  template  = File.join *chunks.map(&:to_s)
  locations = []
  {
    @__rear__templates_path => File.join(app.root, @__rear__templates_path.to_s),
    @__rear__templates_fullpath => @__rear__templates_fullpath,
  }.select {|k,v| k}.each_value do |prefix|
    locations << File.join(prefix, EUtils.class_to_route(self.class.model)) if self.class.respond_to?(:model)
    locations << File.join(prefix, 'shared-templates')
  end
  locations << PATH__TEMPLATES
  locations.each do |p|
    fp = File.join(p, template)
    return explicit_view_path(fp) if File.file?(fp)
  end
  raise ArgumentError, '%s template not found in any of %s paths' % [template,locations*', ']
end

#pkeyObject



24
25
26
# File 'lib/rear/helpers/generic.rb', line 24

def pkey
  __rear__.pkey
end

#quick_filter?(column) ⇒ Boolean

Returns:

  • (Boolean)


21
22
23
# File 'lib/rear/helpers/filters.rb', line 21

def quick_filter? column
  opted_quick_filters[column]
end

#quick_filtersObject



8
9
10
# File 'lib/rear/helpers/filters.rb', line 8

def quick_filters
  __rear__.quick_filters
end

#readonly?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/rear/helpers/generic.rb', line 68

def readonly?
  self.class.readonly?
end

#reander(*args, &proc) ⇒ Object



4
5
6
# File 'lib/rear/helpers/render.rb', line 4

def reander *args, &proc
  reander_layout(:layout) { render_slim args, &proc }
end

#reander_layout(path, *rest, &proc) ⇒ Object Also known as: reander_l



13
14
15
# File 'lib/rear/helpers/render.rb', line 13

def reander_layout path, *rest, &proc
  render_slim_l path_to_rear_templates('%s.slim' % path), *rest, &proc
end

#reander_partial(path, *rest) ⇒ Object Also known as: reander_p



8
9
10
# File 'lib/rear/helpers/render.rb', line 8

def reander_partial path, *rest
  render_slim_p path_to_rear_templates('%s.slim' % path), *rest
end

#render_column(column, scope) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/rear/helpers/columns.rb', line 45

def render_column column, scope
  template, value = %w[template value].map {|m| column.send('%s_%s' % [scope, m])}
  locals = if column.optioned?
    options = column.options
    options = Hash[options.zip(options)] if options.is_a?(Array)
    active_options = self.instance_exec(&column.active_options)
    active_options = [active_options] unless active_options.is_a?(Array)
    {options: options, active_options: active_options}
  else
    {value: self.instance_exec(&value)}
  end
  template.is_a?(Proc) ?
    self.instance_exec(&template) :
    render_slim_p(locals) { template_cache(path_to_rear_templates template) }
end

#render_editor_column(column) ⇒ Object



41
42
43
# File 'lib/rear/helpers/columns.rb', line 41

def render_editor_column column
  render_column column, :editor
end

#render_filtersObject



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/rear/helpers/filters.rb', line 114

def render_filters
  html = assets_mapper(route(:assets), suffix: '-rear').js_tag('xhr')
  main_filters = filters.inject(html) do |html,(column,setups)|
    setups.each_pair do |comparison_function, setup|
      context = {
          name: 'filters[%s][%s]' % [column, comparison_function],
        column: column,
         value: filter(column, comparison_function),
         setup: setup,
         attrs: Hash[setup[:attrs]]
      }
      template = template_cache(path_to_rear_templates(setup[:template]))
      html << render_slim_p(context) { template }
    end
    html << '&nbsp;'
  end
end

#render_pane_column(column) ⇒ Object



37
38
39
# File 'lib/rear/helpers/columns.rb', line 37

def render_pane_column column
  render_column column, :pane
end

#sequel?Boolean

Returns:

  • (Boolean)


20
21
22
# File 'lib/rear/helpers/generic.rb', line 20

def sequel?
  @@__rear__is_sequel ||= RearUtils.orm(model) == :sq
end

#sortable_column?(column) ⇒ Boolean

Returns:

  • (Boolean)


61
62
63
64
65
66
# File 'lib/rear/helpers/columns.rb', line 61

def sortable_column? column
  if column.name == pkey || column.order_by? ||
    __rear__.real_columns.any? {|(n,t)| n == column.name}
    sortable_vector column
  end
end

#sortable_vector(column) ⇒ Object

flip-flopping vectors, that’s it, if current vector set to asc this method will return desc, and vice-versa. also it will return UI arrow direction - when vector is asc arrow is down and arrow is up when vector is desc



8
9
10
11
# File 'lib/rear/helpers/order.rb', line 8

def sortable_vector column
  vector = ['asc', nil].include?(order_params[column.string_name]) ? 'desc' : 'asc'
  [vector, vector == 'asc' ? 'down' : 'up']
end

#sortable_vector?(column) ⇒ Boolean

checks whether ordering is happening by given column and if it is, check whether a valid vector used

Returns:

  • (Boolean)


15
16
17
18
19
# File 'lib/rear/helpers/order.rb', line 15

def sortable_vector? column
  if vector = (params[:order] || {})[column.string_name]
    valid_sortable_vector? vector
  end
end

#template_cache(template) ⇒ Object



35
36
37
# File 'lib/rear/helpers/generic.rb', line 35

def template_cache template
  ((@__rear__template_cache ||= {})[template] ||= {})[File.mtime(template)] ||= File.read(template)
end

#valid_sortable_vector?(vector) ⇒ Boolean

Returns:

  • (Boolean)


21
22
23
# File 'lib/rear/helpers/order.rb', line 21

def valid_sortable_vector? vector
  vector if vector == 'asc' || vector == 'desc'
end