Class: S2P::Search

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Validations
Defined in:
lib/s2p/search.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ Search

Returns a new instance of Search.



196
197
198
199
200
201
202
203
204
205
# File 'lib/s2p/search.rb', line 196

def initialize(params={})
  @params     = params.symbolize_keys
  @conditions = self.class.conditions.select { |k,v| @params[k].present? }

  @sort_by_key = @params.fetch(:order_by, self.class.sort_by_default_key).to_sym
  @sort_order  = @params.fetch(:sort_order, self.class.sort_order_defaults[@sort_by_key]).to_sym

  # FIXME: Should this be a validation instead?
  setup(**params) if respond_to?(:setup)
end

Class Attribute Details

.fromObject (readonly)

Returns the value of attribute from.



191
192
193
# File 'lib/s2p/search.rb', line 191

def from
  @from
end

Instance Attribute Details

#conditionsObject (readonly)

Returns the value of attribute conditions.



194
195
196
# File 'lib/s2p/search.rb', line 194

def conditions
  @conditions
end

#paramsObject (readonly)

Returns the value of attribute params.



194
195
196
# File 'lib/s2p/search.rb', line 194

def params
  @params
end

#resultsObject (readonly)

Returns the value of attribute results.



194
195
196
# File 'lib/s2p/search.rb', line 194

def results
  @results
end

#scopesObject (readonly)

Returns the value of attribute scopes.



194
195
196
# File 'lib/s2p/search.rb', line 194

def scopes
  @scopes
end

Class Method Details

.additional_search_params(*keys) ⇒ Object



18
19
20
# File 'lib/s2p/search.rb', line 18

def additional_search_params(*keys)
  @additional_search_params = keys
end

.cond(key, &scope) ⇒ Object



89
90
91
# File 'lib/s2p/search.rb', line 89

def cond(key, &scope)
  conditions[key] = scope
end

.cond_cont(*keys) ⇒ Object



170
171
172
173
174
# File 'lib/s2p/search.rb', line 170

def cond_cont(*keys)
  keys.each do |key|
    cond(key) { |s,v| s.where("#{from.table_name}.#{key} like ?", "%#{v}%") }
  end
end

.cond_eq(*keys) ⇒ Object



97
98
99
100
101
# File 'lib/s2p/search.rb', line 97

def cond_eq(*keys)
  keys.each do |key|
    cond_eq!(key, key)
  end
end

.cond_eq!(key, column) ⇒ Object



103
104
105
# File 'lib/s2p/search.rb', line 103

def cond_eq!(key, column)
  cond(key) { |s,v| s.where(column => v) }
end

.cond_gt(*keys) ⇒ Object



153
154
155
156
157
# File 'lib/s2p/search.rb', line 153

def cond_gt(*keys)
  keys.each do |key|
    cond(key) { |s,v| s.where("#{from.table_name}.#{key} > ?", v) }
  end
end

.cond_gte(*keys) ⇒ Object



143
144
145
146
147
# File 'lib/s2p/search.rb', line 143

def cond_gte(*keys)
  keys.each do |key|
    cond_gte!(key, "#{from.table_name}.#{key}")
  end
end

.cond_gte!(key, column) ⇒ Object



149
150
151
# File 'lib/s2p/search.rb', line 149

def cond_gte!(key, column)
  cond(key) { |s,v| s.where("#{column} >= ?", v) }
end

.cond_like(*keys) ⇒ Object



117
118
119
120
121
# File 'lib/s2p/search.rb', line 117

def cond_like(*keys)
  keys.each do |key|
    cond_like!(key, "#{from.table_name}.#{key}")
  end
end

.cond_like!(key, column) ⇒ Object



123
124
125
# File 'lib/s2p/search.rb', line 123

def cond_like!(key, column)
  cond(key) { |s,v| s.where("#{column} like ?", "%#{v}%") }
end

.cond_localized_date_range(*ranges) ⇒ Object



176
177
178
179
180
# File 'lib/s2p/search.rb', line 176

def cond_localized_date_range(*ranges)
  ranges.each do |range|
    cond_localized_date_range!(*range)
  end
end

.cond_localized_date_range!(start_key, end_key, column) ⇒ Object

NOTE: You can use cond_range instead of this method when working with date columns in the DB as they’re zoneless. This method is only necessary when working with datetime fields, because for those you will need to translate the beginning/end of the range to the localized beginning/end of day times.



186
187
188
189
# File 'lib/s2p/search.rb', line 186

def cond_localized_date_range!(start_key, end_key, column)
  cond(start_key) { |s,v| s.where("#{from.table_name}.#{column} >= ?", PetersenToolbox.to_local_time(Date.parse(v)).beginning_of_day) }
  cond(end_key) { |s,v| s.where("#{from.table_name}.#{column} <= ?", PetersenToolbox.to_local_time(Date.parse(v)).end_of_day) }
end

.cond_lt(*keys) ⇒ Object



127
128
129
130
131
# File 'lib/s2p/search.rb', line 127

def cond_lt(*keys)
  keys.each do |key|
    cond(key) { |s,v| s.where("#{from.table_name}.#{key} < ?", v) }
  end
end

.cond_lte(*keys) ⇒ Object



133
134
135
136
137
# File 'lib/s2p/search.rb', line 133

def cond_lte(*keys)
  keys.each do |key|
    cond_lte!(key, "#{from.table_name}.#{key}")
  end
end

.cond_lte!(key, column) ⇒ Object



139
140
141
# File 'lib/s2p/search.rb', line 139

def cond_lte!(key, column)
  cond(key) { |s,v| s.where("#{column} <= ?", v) }
end

.cond_range(*ranges) ⇒ Object



159
160
161
162
163
# File 'lib/s2p/search.rb', line 159

def cond_range(*ranges)
  ranges.each do |start_key, end_key, column|
    cond_range!(start_key, end_key, column)
  end
end

.cond_range!(start_key, end_key, column) ⇒ Object



165
166
167
168
# File 'lib/s2p/search.rb', line 165

def cond_range!(start_key, end_key, column)
  cond_gte!(start_key, column)
  cond_lte!(end_key, column)
end

.cond_start(*keys) ⇒ Object



107
108
109
110
111
# File 'lib/s2p/search.rb', line 107

def cond_start(*keys)
  keys.each do |key|
    cond_start!(key, "#{from.table_name}.#{key}")
  end
end

.cond_start!(key, column) ⇒ Object



113
114
115
# File 'lib/s2p/search.rb', line 113

def cond_start!(key, column)
  cond(key) { |s,v| s.where("#{column} like ?", "#{v}%") }
end

.conditionsObject



93
94
95
# File 'lib/s2p/search.rb', line 93

def conditions
  @conditions ||= {}
end

.create(params = {}) ⇒ Object



22
23
24
# File 'lib/s2p/search.rb', line 22

def create(params={})
  new(params).tap(&:save)
end

.default_sort_order(params) ⇒ Object



81
82
83
# File 'lib/s2p/search.rb', line 81

def default_sort_order(params)
  sort_order_defaults.update(params).symbolize_keys!
end

.scopeObject



10
11
12
# File 'lib/s2p/search.rb', line 10

def scope
  @before_callback.present? ? @before_callback[from] : from
end

.search_keysObject



14
15
16
# File 'lib/s2p/search.rb', line 14

def search_keys
  [:page, :sort_order, :order_by] + conditions.keys + (@additional_search_params || [])
end

.searches(from) ⇒ Object



26
27
28
# File 'lib/s2p/search.rb', line 26

def searches(from)
  @from ||= from
end

.setup(&conds) ⇒ Object



6
7
8
# File 'lib/s2p/search.rb', line 6

def setup(&conds)
  @before_callback = conds
end

.sort_by_default(key) ⇒ Object



73
74
75
# File 'lib/s2p/search.rb', line 73

def sort_by_default(key)
  @sort_by_default_key = key
end

.sort_by_default_keyObject



77
78
79
# File 'lib/s2p/search.rb', line 77

def sort_by_default_key
  @sort_by_default_key || :id
end

.sort_by_filtersObject

FIXME: Consider introducing a SortFilter object of some sort?



65
66
67
68
69
70
71
# File 'lib/s2p/search.rb', line 65

def sort_by_filters
  @sort_by_filters ||= Hash.new { |h,k| h[k] = {} }
  @sort_by_filters[:id][:asc]  ||= ->(s) { s.reorder(id: :asc) }
  @sort_by_filters[:id][:desc] ||= ->(s) { s.reorder(id: :desc) }

  @sort_by_filters
end

.sort_order_defaultsObject



85
86
87
# File 'lib/s2p/search.rb', line 85

def sort_order_defaults
  @sort_order_defaults ||=  Hash.new { |h,k| h[k] = :asc }
end

.sortable_by(*names, table_name: self.from.table_name) ⇒ Object



30
31
32
33
34
35
36
37
# File 'lib/s2p/search.rb', line 30

def sortable_by(*names, table_name: self.from.table_name)
  names.each do |name|
    sortable_by!(name,
      asc: ->(s) { s.reorder("#{table_name}.#{name} asc")  },
      desc: ->(s) { s.reorder("#{table_name}.#{name} desc") }
    )
  end
end

.sortable_by!(name, asc:, desc:) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/s2p/search.rb', line 46

def sortable_by!(name, asc:, desc:)
  name = name.to_sym

  case asc
  when String
    sort_by_filters[name][:asc] = ->(s) { s.reorder(asc) }
  when Proc
    sort_by_filters[name][:asc] = asc
  end

  case desc
  when String
    sort_by_filters[name][:desc] = ->(s) { s.reorder(desc) }
  when Proc
    sort_by_filters[name][:desc] = desc
  end
end

.sortable_by_alias(sort_alias, sort_condition) ⇒ Object



39
40
41
42
43
44
# File 'lib/s2p/search.rb', line 39

def sortable_by_alias(sort_alias, sort_condition)
  sortable_by!(sort_alias,
    asc:  ->(s) { s.reorder("#{sort_condition} asc")  },
    desc: ->(s) { s.reorder("#{sort_condition} desc") }
  )
end

Instance Method Details

#saveObject



207
208
209
210
211
212
213
214
215
# File 'lib/s2p/search.rb', line 207

def save
  if valid?
    @results = @conditions.inject(self.class.scope) { |relation, (key, scope)|
      scope.call(relation, @params[key], @params)
    }

    @results = sorted(@results)
  end
end

#sorted(relation) ⇒ Object



217
218
219
# File 'lib/s2p/search.rb', line 217

def sorted(relation)
  self.class.sort_by_filters[@sort_by_key][@sort_order][relation]
end

#to_sObject



221
222
223
# File 'lib/s2p/search.rb', line 221

def to_s
  @params.select { |k,v| v.present? }.map { |k,v| "<strong>#{k.to_s.humanize}</strong>: #{v}" }.join(" + ")
end