Module: Tabulatr::Finder

Defined in:
lib/tabulatr/tabulatr/finder.rb,
lib/tabulatr/tabulatr/finder/find_for_mongoid_table.rb,
lib/tabulatr/tabulatr/finder/find_for_active_record_table.rb

Overview

These are extensions for use from ActionController instances In a seperate class call only for clearity

Defined Under Namespace

Classes: Invoker

Class Method Summary collapse

Class Method Details

.compress_id_list(list) ⇒ Object

compress the list of ids as good as I could imagine ;) uses fancy base twisting



32
33
34
# File 'lib/tabulatr/tabulatr/finder.rb', line 32

def self.compress_id_list(list)
  IdStuffer.stuff(list)
end

.find_for_active_record_table(klaz, params, o = {}, &block) ⇒ Object


Called if SomeActveRecordSubclass::find_for_table(params) is called



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/tabulatr/tabulatr/finder/find_for_active_record_table.rb', line 31

def self.find_for_active_record_table(klaz, params, o={}, &block)
  form_options = Tabulatr.table_form_options
  cname = class_to_param(klaz)
  params ||= {}
  opts = Tabulatr.finder_options.merge(o)
  # before we do anything else, we find whether there's something to do for batch actions
  checked_param = ActiveSupport::HashWithIndifferentAccess.new({:checked_ids => '', :current_page => []}).
    merge(params["#{cname}#{form_options[:checked_postfix]}"] || {})
  checked_ids = uncompress_id_list(checked_param[:checked_ids])
  new_ids = checked_param[:current_page]
  selected_ids = checked_ids + new_ids
  batch_param = params["#{cname}#{form_options[:batch_postfix]}"]
  if batch_param.present? and block_given?
    batch_param = batch_param.keys.first.to_sym if batch_param.is_a?(Hash)
    yield(Invoker.new(batch_param, selected_ids))
  end

  # firstly, get the conditions from the filters
  includes = []
  filter_param = (params["#{cname}#{form_options[:filter_postfix]}"] || {})
  precondition = opts[:precondition] || "(1=1)"
  conditions = filter_param.inject([precondition.dup, []]) do |c, t|
    n, v = t
    # FIXME n = name_escaping(n)
    if (n != form_options[:associations_filter])
      condition_from("#{klaz.table_name}.#{n}",v,c)
    else
      v.inject(c) do |c,t|
        n,v = t
        assoc, att = n.split(".").map(&:to_sym)
        r = klaz.reflect_on_association(assoc)
        includes << assoc
        tname = r.table_name
        nn = "#{tname}.#{att}"
        condition_from(nn,v,c)
      end
    end
  end
  conditions = [conditions.first] + conditions.last

  # secondly, find the order_by stuff
  sortparam = params["#{cname}#{form_options[:sort_postfix]}"]
  if sortparam
    if sortparam[:_resort]
      order_by = sortparam[:_resort].first.first
      order_direction = sortparam[:_resort].first.last.first.first
    else
      order_by = sortparam.first.first
      order_direction = sortparam.first.last.first.first
    end
    raise "SECURITY violation, sort field name is '#{n}'" unless /^[\w]+$/.match order_direction
    raise "SECURITY violation, sort field name is '#{n}'" unless /^[\d\w]+$/.match order_by
    order = "#{order_by} #{order_direction}"
  else
    if opts[:default_order]
      l = opts[:default_order].split(" ")
      raise(":default_order parameter should be of the form 'id asc' or 'name desc'.") \
        if l.length == 0 or l.length > 2
      order_by = l[0]
      order_direction = l[1] || 'asc'
      order = "#{order_by} #{order_direction}"
    else
      order = order_by = order_direction = nil
    end
  end

  # thirdly, get the pagination data
  pops = params["#{cname}#{form_options[:pagination_postfix]}"] || {}
  paginate_options = Tabulatr.paginate_options.merge(opts).merge(pops)
  pagesize = (pops[:pagesize] || opts[:default_pagesize] || paginate_options[:pagesize]).to_f
  page = paginate_options[:page].to_i
  page += 1 if paginate_options[:page_right]
  page -= 1 if paginate_options[:page_left]
  c = klaz.count :conditions => conditions, :include => includes
  pages = (c/pagesize).ceil
  page = [1, [page, pages].min].max

  # then, we obey any "select" buttons if pushed
  if checked_param[:select_all]
    all = klaz.find :all, :conditions => precondition, :select => :id
    selected_ids = all.map { |r| r.id.to_s }
  elsif checked_param[:select_none]
    selected_ids = []
  elsif checked_param[:select_visible]
    visible_ids = uncompress_id_list(checked_param[:visible])
    selected_ids = (selected_ids + visible_ids).sort.uniq
  elsif checked_param[:unselect_visible]
    visible_ids = uncompress_id_list(checked_param[:visible])
    selected_ids = (selected_ids - visible_ids).sort.uniq
  elsif checked_param[:select_filtered]
    all = klaz.find :all, :conditions => conditions, :select => :id, :include => includes
    selected_ids = (selected_ids + all.map { |r| r.id.to_s }).sort.uniq
  elsif checked_param[:unselect_filtered]
    all = klaz.find :all, :conditions => conditions, :select => :id, :include => includes
    selected_ids = (selected_ids - all.map { |r| r.id.to_s }).sort.uniq
  end


  # Now, actually find the stuff
  found = klaz.find :all, :conditions => conditions,
    :limit => pagesize.to_i, :offset => ((page-1)*pagesize).to_i,
    :order  => order, :include => includes

  # finally, inject methods to retrieve the current 'settings'
  fio = Tabulatr.finder_inject_options
  found.define_singleton_method(fio[:filters]) do filter_param end
  found.define_singleton_method(fio[:classname]) do cname end
  found.define_singleton_method(fio[:pagination]) do
    { :page => page, :pagesize => pagesize, :count => c, :pages => pages,
      :pagesizes => paginate_options[:pagesizes],
      :total => klaz.count(:conditions => precondition) }
  end
  found.define_singleton_method(fio[:sorting]) do
    order ? { :by => order_by, :direction => order_direction } : nil
  end
  visible_ids = (found.map { |r| r.id.to_s })
  checked_ids = compress_id_list(selected_ids - visible_ids)
  visible_ids = compress_id_list(visible_ids)
  found.define_singleton_method(fio[:checked]) do
    { :selected => selected_ids,
      :checked_ids => checked_ids,
      :visible => visible_ids
    }
  end
  found.define_singleton_method(fio[:store_data]) do
    opts[:store_data] || {}
  end

  found
end

.find_for_mongoid_table(klaz, params, opts = {}) ⇒ Object


Called if SomeMongoidDocument::find_for_table(params) is called



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
113
114
# File 'lib/tabulatr/tabulatr/finder/find_for_mongoid_table.rb', line 31

def self.find_for_mongoid_table(klaz, params, opts={})
  # firstly, get the conditions from the filters
  cname = class_to_param(klaz)
  filter_param = (params["#{cname}#{TABLE_FORM_OPTIONS[:filter_postfix]}"] || {})
  conditions = filter_param.inject({}) do |c, t|
    n, v = t
    nc = c
    # FIXME n = name_escaping(n)
    raise "SECURITY violation, field name is '#{n}'" unless /^[\d\w]+$/.match n
    if v.class == String
      if v.present?
        nc[n.to_sym] = v
      end
    elsif v.is_a?(Hash)
      if v[:like]
        if v[:like].present?
          nc[n.to_sym] = Regexp.new("#{v[:like]}")
        end
      else
        nc[n.to_sym.gte] = "#{v[:from]}" if v[:from].present?
        nc[n.to_sym.lte] = "#{v[:to]}" if v[:to].present?
      end
    else
      raise "Wrong filter type: #{v.class}"
    end
    nc
  end

  # secondly, find the order_by stuff
  # FIXME: Implement me! PLEEEZE!
  sortparam = params["#{cname}#{TABLE_FORM_OPTIONS[:sort_postfix]}"]
  if sortparam
    if sortparam[:_resort]
      order_by = sortparam[:_resort].first.first
      order_direction = sortparam[:_resort].first.last.first.first
    else
      order_by = sortparam.first.first
      order_direction = sortparam.first.last.first.first
    end
    raise "SECURITY violation, sort field name is '#{n}'" unless /^[\w]+$/.match order_direction
    raise "SECURITY violation, sort field name is '#{n}'" unless /^[\d\w]+$/.match order_by
    order = [order_by, order_direction]
  else
    order = nil
  end

  # thirdly, get the pagination data
  paginate_options = PAGINATE_OPTIONS.merge(opts).
    merge(params["#{cname}#{TABLE_FORM_OPTIONS[:pagination_postfix]}"] || {})
  page = paginate_options[:page].to_i
  page += 1 if paginate_options[:page_right]
  page -= 1 if paginate_options[:page_left]
  pagesize = paginate_options[:pagesize].to_f
  c = klaz.count :conditions => conditions
  pages = (c/pagesize).ceil
  page = [1, [page, pages].min].max

  # Now, actually find the stuff
  found = klaz.find(:conditions => conditions)
  found = found.order_by([order]) if order
  found = found.paginate(:page => page, :per_page => pagesize)

  # finally, inject methods to retrieve the current 'settings'
  found.define_singleton_method(fio[:filters]) do filter_param end
  found.define_singleton_method(fio[:classname]) do cname end
  found.define_singleton_method(fio[:pagination]) do
    {:page => page, :pagesize => pagesize, :count => c, :pages => pages,
      :pagesizes => paginate_options[:pagesizes]}
  end
  found.define_singleton_method(fio[:sorting]) do
    order ? { :by => order_by, :direction => order_direction } : nil
  end
  checked_param = params["#{cname}#{TABLE_FORM_OPTIONS[:checked_postfix]}"]
  checked_ids = checked_param[:checked].split(TABLE_FORM_OPTIONS[:checked_separator])
  new_ids = checked_param[:current_page] || []
  selected_ids = checked_ids + new_ids
  ids = found.map { |r| r.id.to_s }
  checked_ids = selected_ids - ids
  found.define_singleton_method(fio[:checked]) do
    { :selected => selected_ids,
      :checked_ids => checked_ids.join(TABLE_FORM_OPTIONS[:checked_separator]) }
  end
  found
end

.uncompress_id_list(str) ⇒ Object

inverse of compress_id_list



37
38
39
# File 'lib/tabulatr/tabulatr/finder.rb', line 37

def self.uncompress_id_list(str)
  IdStuffer.unstuff(str).map &:to_s
end