mini-fast-sys 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/mini-fast-sys.gemspec +12 -0
  3. data/ransack-4.4.1/LICENSE +20 -0
  4. data/ransack-4.4.1/README.md +101 -0
  5. data/ransack-4.4.1/lib/polyamorous/activerecord/join_association.rb +70 -0
  6. data/ransack-4.4.1/lib/polyamorous/activerecord/join_association_7_2.rb +55 -0
  7. data/ransack-4.4.1/lib/polyamorous/activerecord/join_dependency.rb +102 -0
  8. data/ransack-4.4.1/lib/polyamorous/activerecord/reflection.rb +11 -0
  9. data/ransack-4.4.1/lib/polyamorous/join.rb +70 -0
  10. data/ransack-4.4.1/lib/polyamorous/polyamorous.rb +27 -0
  11. data/ransack-4.4.1/lib/polyamorous/swapping_reflection_class.rb +11 -0
  12. data/ransack-4.4.1/lib/polyamorous/tree_node.rb +7 -0
  13. data/ransack-4.4.1/lib/ransack/active_record.rb +14 -0
  14. data/ransack-4.4.1/lib/ransack/adapters/active_record/base.rb +152 -0
  15. data/ransack-4.4.1/lib/ransack/adapters/active_record/context.rb +373 -0
  16. data/ransack-4.4.1/lib/ransack/configuration.rb +209 -0
  17. data/ransack-4.4.1/lib/ransack/constants.rb +176 -0
  18. data/ransack-4.4.1/lib/ransack/context.rb +190 -0
  19. data/ransack-4.4.1/lib/ransack/helpers/form_builder.rb +277 -0
  20. data/ransack-4.4.1/lib/ransack/helpers/form_helper.rb +311 -0
  21. data/ransack-4.4.1/lib/ransack/helpers.rb +2 -0
  22. data/ransack-4.4.1/lib/ransack/invalid_search_error.rb +3 -0
  23. data/ransack-4.4.1/lib/ransack/locale/ar.yml +70 -0
  24. data/ransack-4.4.1/lib/ransack/locale/az.yml +70 -0
  25. data/ransack-4.4.1/lib/ransack/locale/bg.yml +70 -0
  26. data/ransack-4.4.1/lib/ransack/locale/ca.yml +70 -0
  27. data/ransack-4.4.1/lib/ransack/locale/cs.yml +70 -0
  28. data/ransack-4.4.1/lib/ransack/locale/da.yml +70 -0
  29. data/ransack-4.4.1/lib/ransack/locale/de.yml +70 -0
  30. data/ransack-4.4.1/lib/ransack/locale/el.yml +70 -0
  31. data/ransack-4.4.1/lib/ransack/locale/en.yml +70 -0
  32. data/ransack-4.4.1/lib/ransack/locale/es.yml +70 -0
  33. data/ransack-4.4.1/lib/ransack/locale/fa.yml +70 -0
  34. data/ransack-4.4.1/lib/ransack/locale/fi.yml +71 -0
  35. data/ransack-4.4.1/lib/ransack/locale/fr.yml +70 -0
  36. data/ransack-4.4.1/lib/ransack/locale/hu.yml +70 -0
  37. data/ransack-4.4.1/lib/ransack/locale/id.yml +70 -0
  38. data/ransack-4.4.1/lib/ransack/locale/it.yml +70 -0
  39. data/ransack-4.4.1/lib/ransack/locale/ja.yml +70 -0
  40. data/ransack-4.4.1/lib/ransack/locale/ko.yml +70 -0
  41. data/ransack-4.4.1/lib/ransack/locale/nl.yml +70 -0
  42. data/ransack-4.4.1/lib/ransack/locale/pt-BR.yml +70 -0
  43. data/ransack-4.4.1/lib/ransack/locale/ro.yml +70 -0
  44. data/ransack-4.4.1/lib/ransack/locale/ru.yml +70 -0
  45. data/ransack-4.4.1/lib/ransack/locale/sk.yml +70 -0
  46. data/ransack-4.4.1/lib/ransack/locale/sv.yml +70 -0
  47. data/ransack-4.4.1/lib/ransack/locale/tr.yml +70 -0
  48. data/ransack-4.4.1/lib/ransack/locale/uk.yml +72 -0
  49. data/ransack-4.4.1/lib/ransack/locale/zh-CN.yml +70 -0
  50. data/ransack-4.4.1/lib/ransack/locale/zh-TW.yml +70 -0
  51. data/ransack-4.4.1/lib/ransack/naming.rb +58 -0
  52. data/ransack-4.4.1/lib/ransack/nodes/attribute.rb +59 -0
  53. data/ransack-4.4.1/lib/ransack/nodes/bindable.rb +52 -0
  54. data/ransack-4.4.1/lib/ransack/nodes/condition.rb +399 -0
  55. data/ransack-4.4.1/lib/ransack/nodes/grouping.rb +206 -0
  56. data/ransack-4.4.1/lib/ransack/nodes/node.rb +34 -0
  57. data/ransack-4.4.1/lib/ransack/nodes/sort.rb +54 -0
  58. data/ransack-4.4.1/lib/ransack/nodes/value.rb +115 -0
  59. data/ransack-4.4.1/lib/ransack/predicate.rb +75 -0
  60. data/ransack-4.4.1/lib/ransack/ransacker.rb +24 -0
  61. data/ransack-4.4.1/lib/ransack/search.rb +195 -0
  62. data/ransack-4.4.1/lib/ransack/translate.rb +156 -0
  63. data/ransack-4.4.1/lib/ransack/version.rb +3 -0
  64. data/ransack-4.4.1/lib/ransack/visitor.rb +87 -0
  65. data/ransack-4.4.1/lib/ransack.rb +33 -0
  66. data/ransack-4.4.1/spec/console.rb +13 -0
  67. data/ransack-4.4.1/spec/factories/articles.rb +7 -0
  68. data/ransack-4.4.1/spec/factories/comments.rb +7 -0
  69. data/ransack-4.4.1/spec/factories/notes.rb +13 -0
  70. data/ransack-4.4.1/spec/factories/people.rb +10 -0
  71. data/ransack-4.4.1/spec/factories/tags.rb +5 -0
  72. data/ransack-4.4.1/spec/helpers/polyamorous_helper.rb +13 -0
  73. data/ransack-4.4.1/spec/helpers/ransack_helper.rb +9 -0
  74. data/ransack-4.4.1/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  75. data/ransack-4.4.1/spec/polyamorous/join_association_spec.rb +29 -0
  76. data/ransack-4.4.1/spec/polyamorous/join_dependency_spec.rb +80 -0
  77. data/ransack-4.4.1/spec/polyamorous/join_spec.rb +19 -0
  78. data/ransack-4.4.1/spec/ransack/adapters/active_record/base_spec.rb +943 -0
  79. data/ransack-4.4.1/spec/ransack/adapters/active_record/context_spec.rb +219 -0
  80. data/ransack-4.4.1/spec/ransack/configuration_spec.rb +201 -0
  81. data/ransack-4.4.1/spec/ransack/helpers/form_builder_spec.rb +169 -0
  82. data/ransack-4.4.1/spec/ransack/helpers/form_helper_spec.rb +1075 -0
  83. data/ransack-4.4.1/spec/ransack/invalid_search_error_spec.rb +27 -0
  84. data/ransack-4.4.1/spec/ransack/nodes/condition_spec.rb +333 -0
  85. data/ransack-4.4.1/spec/ransack/nodes/grouping_spec.rb +111 -0
  86. data/ransack-4.4.1/spec/ransack/nodes/value_spec.rb +126 -0
  87. data/ransack-4.4.1/spec/ransack/predicate_spec.rb +504 -0
  88. data/ransack-4.4.1/spec/ransack/ransacker_spec.rb +69 -0
  89. data/ransack-4.4.1/spec/ransack/search_spec.rb +821 -0
  90. data/ransack-4.4.1/spec/ransack/translate_spec.rb +16 -0
  91. data/ransack-4.4.1/spec/spec_helper.rb +52 -0
  92. data/ransack-4.4.1/spec/support/en.yml +18 -0
  93. data/ransack-4.4.1/spec/support/schema.rb +419 -0
  94. metadata +133 -0
@@ -0,0 +1,399 @@
1
+ require 'ransack/invalid_search_error'
2
+
3
+ module Ransack
4
+ module Nodes
5
+ class Condition < Node
6
+ i18n_word :attribute, :predicate, :combinator, :value
7
+ i18n_alias a: :attribute, p: :predicate,
8
+ m: :combinator, v: :value
9
+
10
+ attr_accessor :predicate
11
+
12
+ class << self
13
+ def extract(context, key, values)
14
+ attributes, predicate, combinator =
15
+ extract_values_for_condition(key, context)
16
+
17
+ if attributes.size > 0 && predicate
18
+ condition = self.new(context)
19
+ condition.build(
20
+ a: attributes,
21
+ p: predicate.name,
22
+ m: combinator,
23
+ v: predicate.wants_array ? Array(values) : [values]
24
+ )
25
+ # TODO: Figure out what to do with multiple types of attributes,
26
+ # if anything. Tempted to go with "garbage in, garbage out" here.
27
+ if predicate.validate(condition.values, condition.default_type)
28
+ condition
29
+ else
30
+ nil
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def extract_values_for_condition(key, context = nil)
38
+ str = key.dup
39
+ name = Predicate.detect_and_strip_from_string!(str)
40
+ predicate = Predicate.named(name)
41
+
42
+ unless predicate || Ransack.options[:ignore_unknown_conditions]
43
+ raise InvalidSearchError, "No valid predicate for #{key}"
44
+ end
45
+
46
+ if context.present?
47
+ str = context.ransackable_alias(str)
48
+ end
49
+
50
+ combinator =
51
+ if str.match(/_(or|and)_/)
52
+ $1
53
+ else
54
+ nil
55
+ end
56
+
57
+ if context.present? && context.attribute_method?(str)
58
+ attributes = [str]
59
+ else
60
+ attributes = str.split(/_and_|_or_/)
61
+ end
62
+
63
+ [attributes, predicate, combinator]
64
+ end
65
+ end
66
+
67
+ def valid?
68
+ attributes.detect(&:valid?) && predicate && valid_arity? &&
69
+ predicate.validate(values, default_type) && valid_combinator?
70
+ end
71
+
72
+ def valid_arity?
73
+ values.size <= 1 || predicate.wants_array
74
+ end
75
+
76
+ def attributes
77
+ @attributes ||= []
78
+ end
79
+ alias :a :attributes
80
+
81
+ def attributes=(args)
82
+ case args
83
+ when Array
84
+ args.each do |name|
85
+ build_attribute(name)
86
+ end
87
+ when Hash
88
+ args.each do |index, attrs|
89
+ build_attribute(attrs[:name], attrs[:ransacker_args])
90
+ end
91
+ else
92
+ raise ArgumentError,
93
+ "Invalid argument (#{args.class}) supplied to attributes="
94
+ end
95
+ end
96
+ alias :a= :attributes=
97
+
98
+ def values
99
+ @values ||= []
100
+ end
101
+ alias :v :values
102
+
103
+ def values=(args)
104
+ case args
105
+ when Array
106
+ args.each do |val|
107
+ val = Value.new(@context, val)
108
+ self.values << val
109
+ end
110
+ when Hash
111
+ args.each do |index, attrs|
112
+ val = Value.new(@context, attrs[:value])
113
+ self.values << val
114
+ end
115
+ else
116
+ raise ArgumentError,
117
+ "Invalid argument (#{args.class}) supplied to values="
118
+ end
119
+ end
120
+ alias :v= :values=
121
+
122
+ def combinator
123
+ @attributes.size > 1 ? @combinator : nil
124
+ end
125
+
126
+ def combinator=(val)
127
+ @combinator = Constants::AND_OR.detect { |v| v == val.to_s } || nil
128
+ end
129
+ alias :m= :combinator=
130
+ alias :m :combinator
131
+
132
+ # == build_attribute
133
+ #
134
+ # This method was originally called from Nodes::Grouping#new_condition
135
+ # only, without arguments, without #valid? checking, to build a new
136
+ # grouping condition.
137
+ #
138
+ # After refactoring in 235eae3, it is now called from 2 places:
139
+ #
140
+ # 1. Nodes::Condition#attributes=, with +name+ argument passed or +name+
141
+ # and +ransacker_args+. Attributes are included only if #valid?.
142
+ #
143
+ # 2. Nodes::Grouping#new_condition without arguments. In this case, the
144
+ # #valid? conditional needs to be bypassed, otherwise nothing is
145
+ # built. The `name.nil?` conditional below currently does this.
146
+ #
147
+ # TODO: Add test coverage for this behavior and ensure that `name.nil?`
148
+ # isn't fixing issue #701 by introducing untested regressions.
149
+ #
150
+ def build_attribute(name = nil, ransacker_args = [])
151
+ Attribute.new(@context, name, ransacker_args).tap do |attribute|
152
+ @context.bind(attribute, attribute.name)
153
+ self.attributes << attribute if name.nil? || attribute.valid?
154
+ if predicate && !negative?
155
+ @context.lock_association(attribute.parent)
156
+ end
157
+ end
158
+ end
159
+
160
+ def build_value(val = nil)
161
+ Value.new(@context, val).tap do |value|
162
+ self.values << value
163
+ end
164
+ end
165
+
166
+ def value
167
+ if predicate.wants_array
168
+ values.map { |v| v.cast(default_type) }
169
+ else
170
+ values.first.cast(default_type)
171
+ end
172
+ end
173
+
174
+ def build(params)
175
+ params.with_indifferent_access.each do |key, value|
176
+ if key.match(/^(a|v|p|m)$/)
177
+ self.send("#{key}=", value)
178
+ end
179
+ end
180
+
181
+ self
182
+ end
183
+
184
+ def persisted?
185
+ false
186
+ end
187
+
188
+ def key
189
+ @key ||= attributes.map(&:name).join("_#{combinator}_") +
190
+ "_#{predicate.name}"
191
+ end
192
+
193
+ def eql?(other)
194
+ self.class == other.class &&
195
+ self.attributes == other.attributes &&
196
+ self.predicate == other.predicate &&
197
+ self.values == other.values &&
198
+ self.combinator == other.combinator
199
+ end
200
+ alias :== :eql?
201
+
202
+ def hash
203
+ [attributes, predicate, values, combinator].hash
204
+ end
205
+
206
+ def predicate_name=(name)
207
+ self.predicate = Predicate.named(name)
208
+ unless negative?
209
+ attributes.each { |a| context.lock_association(a.parent) }
210
+ end
211
+ @predicate
212
+ end
213
+ alias :p= :predicate_name=
214
+
215
+ def predicate_name
216
+ predicate.name if predicate
217
+ end
218
+ alias :p :predicate_name
219
+
220
+ def arel_predicate
221
+ raise "not implemented"
222
+ end
223
+
224
+ def validated_values
225
+ values.select { |v| predicate.validator.call(v.value) }
226
+ end
227
+
228
+ def casted_values_for_attribute(attr)
229
+ validated_values.map { |v| v.cast(predicate.type || attr.type) }
230
+ end
231
+
232
+ def formatted_values_for_attribute(attr)
233
+ formatted = casted_values_for_attribute(attr).map do |val|
234
+ if attr.ransacker && attr.ransacker.formatter
235
+ val = attr.ransacker.formatter.call(val)
236
+ end
237
+ val = predicate.format(val)
238
+ if val.is_a?(String) && val.include?('%')
239
+ val = Arel::Nodes::Quoted.new(val)
240
+ end
241
+ val
242
+ end
243
+ if predicate.wants_array
244
+ formatted
245
+ else
246
+ formatted.first
247
+ end
248
+ end
249
+
250
+ def arel_predicate_for_attribute(attr)
251
+ if predicate.arel_predicate === Proc
252
+ values = casted_values_for_attribute(attr)
253
+ unless predicate.wants_array
254
+ values = values.first
255
+ end
256
+ predicate.arel_predicate.call(values)
257
+ else
258
+ predicate.arel_predicate
259
+ end
260
+ end
261
+
262
+ def attr_value_for_attribute(attr)
263
+ return attr.attr if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
264
+
265
+ predicate.case_insensitive ? attr.attr.lower : attr.attr
266
+ rescue
267
+ attr.attr
268
+ end
269
+
270
+ def default_type
271
+ predicate.type || (attributes.first && attributes.first.type)
272
+ end
273
+
274
+ def inspect
275
+ data = [
276
+ ['attributes'.freeze, a.try(:map, &:name)],
277
+ ['predicate'.freeze, p],
278
+ [Constants::COMBINATOR, m],
279
+ ['values'.freeze, v.try(:map, &:value)]
280
+ ]
281
+ .reject { |e| e[1].blank? }
282
+ .map { |v| "#{v[0]}: #{v[1]}" }
283
+ .join(', '.freeze)
284
+ "Condition <#{data}>"
285
+ end
286
+
287
+ def negative?
288
+ predicate.negative?
289
+ end
290
+
291
+ def arel_predicate
292
+ predicate = attributes.map { |attribute|
293
+ association = attribute.parent
294
+ parent_table = association.table
295
+
296
+ if negative? && attribute.associated_collection? && not_nested_condition(attribute, parent_table)
297
+ query = context.build_correlated_subquery(association)
298
+ context.remove_association(association)
299
+
300
+ case self.predicate_name
301
+ when 'not_null'
302
+ if self.value
303
+ query.where(format_predicate(attribute))
304
+ Arel::Nodes::In.new(context.primary_key, Arel.sql(query.to_sql))
305
+ else
306
+ query.where(format_predicate(attribute).not)
307
+ Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
308
+ end
309
+ when 'not_cont'
310
+ query.where(attribute.attr.matches(formatted_values_for_attribute(attribute)))
311
+ Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
312
+ else
313
+ query.where(format_predicate(attribute).not)
314
+ Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
315
+ end
316
+ else
317
+ format_predicate(attribute)
318
+ end
319
+ }.reduce(combinator_method)
320
+
321
+ if replace_right_node?(predicate)
322
+ # Replace right node object to plain integer value in order to avoid
323
+ # ActiveModel::RangeError from Arel::Node::Casted.
324
+ # The error can be ignored here because RDBMSs accept large numbers
325
+ # in condition clauses.
326
+ plain_value = predicate.right.value
327
+ predicate.right = plain_value
328
+ end
329
+
330
+ predicate
331
+ end
332
+
333
+ def not_nested_condition(attribute, parent_table)
334
+ parent_table.class != Arel::Nodes::TableAlias && attribute.name.starts_with?(parent_table.name)
335
+ end
336
+
337
+ private
338
+
339
+ def combinator_method
340
+ combinator === Constants::OR ? :or : :and
341
+ end
342
+
343
+ def format_predicate(attribute)
344
+ arel_pred = arel_predicate_for_attribute(attribute)
345
+ arel_values = formatted_values_for_attribute(attribute)
346
+
347
+ # For LIKE predicates, wrap the value in Arel::Nodes.build_quoted to prevent
348
+ # ActiveRecord normalization from affecting wildcard patterns
349
+ if like_predicate?(arel_pred)
350
+ arel_values = Arel::Nodes.build_quoted(arel_values)
351
+ end
352
+
353
+ predicate = attr_value_for_attribute(attribute).public_send(arel_pred, arel_values)
354
+
355
+ if in_predicate?(predicate)
356
+ predicate.right = predicate.right.map do |pr|
357
+ casted_array?(pr) ? format_values_for(pr) : pr
358
+ end
359
+ end
360
+
361
+ predicate
362
+ end
363
+
364
+ def in_predicate?(predicate)
365
+ return unless defined?(Arel::Nodes::Casted)
366
+ predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn
367
+ end
368
+
369
+ def like_predicate?(arel_predicate)
370
+ arel_predicate == 'matches' || arel_predicate == 'does_not_match'
371
+ end
372
+
373
+ def casted_array?(predicate)
374
+ predicate.is_a?(Arel::Nodes::Casted) && predicate.value.is_a?(Array)
375
+ end
376
+
377
+ def format_values_for(predicate)
378
+ predicate.value.map do |val|
379
+ val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val
380
+ end
381
+ end
382
+
383
+ def replace_right_node?(predicate)
384
+ return false unless predicate.is_a?(Arel::Nodes::Binary)
385
+
386
+ arel_node = predicate.right
387
+ return false unless arel_node.is_a?(Arel::Nodes::Casted)
388
+
389
+ relation, name = arel_node.attribute.values
390
+ attribute_type = relation.type_for_attribute(name).type
391
+ attribute_type == :integer && arel_node.value.is_a?(Integer)
392
+ end
393
+
394
+ def valid_combinator?
395
+ attributes.size < 2 || Constants::AND_OR.include?(combinator)
396
+ end
397
+ end
398
+ end
399
+ end
@@ -0,0 +1,206 @@
1
+ module Ransack
2
+ module Nodes
3
+ class Grouping < Node
4
+ attr_reader :conditions
5
+ attr_accessor :combinator
6
+ alias :m :combinator
7
+ alias :m= :combinator=
8
+
9
+ i18n_word :condition, :and, :or
10
+ i18n_alias c: :condition, n: :and, o: :or
11
+
12
+ delegate :each, to: :values
13
+
14
+ def initialize(context, combinator = nil)
15
+ super(context)
16
+ self.combinator = combinator.to_s if combinator
17
+ end
18
+
19
+ def persisted?
20
+ false
21
+ end
22
+
23
+ def translate(key, options = {})
24
+ super or Translate.attribute(
25
+ key.to_s, options.merge(context: context)
26
+ )
27
+ end
28
+
29
+ def conditions
30
+ @conditions ||= []
31
+ end
32
+ alias :c :conditions
33
+
34
+ def conditions=(conditions)
35
+ case conditions
36
+ when Array
37
+ conditions.each do |attrs|
38
+ condition = Condition.new(@context).build(attrs)
39
+ self.conditions << condition if condition.valid?
40
+ end
41
+ when Hash
42
+ conditions.each do |index, attrs|
43
+ condition = Condition.new(@context).build(attrs)
44
+ self.conditions << condition if condition.valid?
45
+ end
46
+ end
47
+ remove_duplicate_conditions!
48
+ end
49
+ alias :c= :conditions=
50
+
51
+ def [](key)
52
+ conditions.detect { |c| c.key == key.to_s }
53
+ end
54
+
55
+ def []=(key, value)
56
+ conditions.reject! { |c| c.key == key.to_s && c&.value == value&.value }
57
+ self.conditions << value
58
+ end
59
+
60
+ def values
61
+ conditions + groupings
62
+ end
63
+
64
+ def respond_to?(method_id)
65
+ super or begin
66
+ method_name = method_id.to_s
67
+ attribute_method?(method_name) ? true : false
68
+ end
69
+ end
70
+
71
+ def build_condition(opts = {})
72
+ new_condition(opts).tap do |condition|
73
+ self.conditions << condition
74
+ end
75
+ end
76
+
77
+ def new_condition(opts = {})
78
+ attrs = opts[:attributes] || 1
79
+ vals = opts[:values] || 1
80
+ condition = Condition.new(@context)
81
+ attrs.times { condition.build_attribute }
82
+ vals.times { condition.build_value }
83
+ condition
84
+ end
85
+
86
+ def groupings
87
+ @groupings ||= []
88
+ end
89
+ alias :g :groupings
90
+
91
+ def groupings=(groupings)
92
+ case groupings
93
+ when Array
94
+ groupings.each do |attrs|
95
+ grouping_object = new_grouping(attrs)
96
+ self.groupings << grouping_object if grouping_object.values.any?
97
+ end
98
+ when Hash
99
+ groupings.each do |index, attrs|
100
+ grouping_object = new_grouping(attrs)
101
+ self.groupings << grouping_object if grouping_object.values.any?
102
+ end
103
+ else
104
+ raise ArgumentError,
105
+ "Invalid argument (#{groupings.class}) supplied to groupings="
106
+ end
107
+ end
108
+ alias :g= :groupings=
109
+
110
+ def method_missing(method_id, *args)
111
+ method_name = method_id.to_s.dup
112
+ writer = method_name.sub!(/\=$/, ''.freeze)
113
+ if attribute_method?(method_name)
114
+ if writer
115
+ write_attribute(method_name, *args)
116
+ else
117
+ read_attribute(method_name)
118
+ end
119
+ else
120
+ super
121
+ end
122
+ end
123
+
124
+ def attribute_method?(name)
125
+ stripped_name = strip_predicate_and_index(name)
126
+ return true if @context.attribute_method?(stripped_name) ||
127
+ @context.attribute_method?(name)
128
+ case stripped_name
129
+ when /^(g|c|m|groupings|conditions|combinator)=?$/
130
+ true
131
+ else
132
+ stripped_name
133
+ .split(/_and_|_or_/)
134
+ .none? { |n| !@context.attribute_method?(n) }
135
+ end
136
+ end
137
+
138
+ def build_grouping(params = {})
139
+ params ||= {}
140
+ new_grouping(params).tap do |new_grouping|
141
+ self.groupings << new_grouping
142
+ end
143
+ end
144
+
145
+ def new_grouping(params = {})
146
+ Grouping.new(@context).build(params)
147
+ end
148
+
149
+ def build(params)
150
+ params.with_indifferent_access.each do |key, value|
151
+ case key
152
+ when /^(g|c|m)$/
153
+ self.send("#{key}=", value)
154
+ else
155
+ write_attribute(key.to_s, value)
156
+ end
157
+ end
158
+ self
159
+ end
160
+
161
+ def inspect
162
+ data = [
163
+ ['conditions'.freeze, conditions], [Constants::COMBINATOR, combinator]
164
+ ]
165
+ .reject { |e| e[1].blank? }
166
+ .map { |v| "#{v[0]}: #{v[1]}" }
167
+ .join(', '.freeze)
168
+ "Grouping <#{data}>"
169
+ end
170
+
171
+ private
172
+
173
+ def write_attribute(name, val)
174
+ # TODO: Methods
175
+ if condition = Condition.extract(@context, name, val)
176
+ self[name] = condition
177
+ end
178
+ end
179
+
180
+ def read_attribute(name)
181
+ if self[name].respond_to?(:value)
182
+ self[name].value
183
+ else
184
+ self[name]
185
+ end
186
+ end
187
+
188
+ def strip_predicate_and_index(str)
189
+ string = str[/(.+?)\(/, 1] || str.dup
190
+ Predicate.detect_and_strip_from_string!(string)
191
+ string
192
+ end
193
+
194
+ def remove_duplicate_conditions!
195
+ # If self.conditions.uniq! is called without passing a block, then
196
+ # conditions differing only by ransacker_args within attributes are
197
+ # wrongly considered equal and are removed.
198
+ self.conditions.uniq! do |c|
199
+ c.attributes.map { |a| [a.name, a.ransacker_args] }.flatten +
200
+ [c.predicate.name] +
201
+ c.values.map { |v| v.value }
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,34 @@
1
+ module Ransack
2
+ module Nodes
3
+ class Node
4
+ attr_reader :context
5
+ delegate :contextualize, to: :context
6
+ class_attribute :i18n_words
7
+ class_attribute :i18n_aliases
8
+ self.i18n_words = []
9
+ self.i18n_aliases = {}
10
+
11
+ class << self
12
+ def i18n_word(*args)
13
+ self.i18n_words += args.map(&:to_s)
14
+ end
15
+
16
+ def i18n_alias(opts = {})
17
+ self.i18n_aliases.merge! Hash[opts.map { |k, v| [k.to_s, v.to_s] }]
18
+ end
19
+ end
20
+
21
+ def initialize(context)
22
+ @context = context
23
+ end
24
+
25
+ def translate(key, options = {})
26
+ key = i18n_aliases[key.to_s] if i18n_aliases.has_key?(key.to_s)
27
+ if i18n_words.include?(key.to_s)
28
+ Translate.word(key)
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,54 @@
1
+ module Ransack
2
+ module Nodes
3
+ class Sort < Node
4
+ include Bindable
5
+
6
+ attr_reader :name, :dir, :ransacker_args
7
+ i18n_word :asc, :desc
8
+
9
+ class << self
10
+ def extract(context, str)
11
+ return if str.blank?
12
+ attr, direction = str.split(/\s+/, 2)
13
+ self.new(context).build(name: attr, dir: direction)
14
+ end
15
+ end
16
+
17
+ def build(params)
18
+ params.with_indifferent_access.each do |key, value|
19
+ if key.match(/^(name|dir|ransacker_args)$/)
20
+ self.send("#{key}=", value)
21
+ end
22
+ end
23
+
24
+ self
25
+ end
26
+
27
+ def valid?
28
+ bound? && attr &&
29
+ context.klassify(parent).ransortable_attributes(context.auth_object)
30
+ .include?(attr_name)
31
+ end
32
+
33
+ def name=(name)
34
+ @name = context.ransackable_alias(name) || name
35
+ context.bind(self, @name)
36
+ end
37
+
38
+ def dir=(dir)
39
+ dir = dir.downcase if dir
40
+ @dir =
41
+ if dir == 'asc'.freeze || dir == 'desc'.freeze
42
+ dir
43
+ else
44
+ 'asc'.freeze
45
+ end
46
+ end
47
+
48
+ def ransacker_args=(ransack_args)
49
+ @ransacker_args = ransack_args
50
+ end
51
+
52
+ end
53
+ end
54
+ end