ransack 1.2.3 → 1.3.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -4
- data/CONTRIBUTING.md +12 -4
- data/Gemfile +4 -5
- data/README.md +160 -55
- data/lib/ransack.rb +1 -1
- data/lib/ransack/adapters/active_record/3.0/context.rb +16 -0
- data/lib/ransack/adapters/active_record/3.1/context.rb +24 -0
- data/lib/ransack/adapters/active_record/base.rb +6 -0
- data/lib/ransack/adapters/active_record/context.rb +49 -1
- data/lib/ransack/configuration.rb +23 -6
- data/lib/ransack/constants.rb +46 -45
- data/lib/ransack/context.rb +19 -2
- data/lib/ransack/helpers/form_builder.rb +5 -4
- data/lib/ransack/helpers/form_helper.rb +34 -14
- data/lib/ransack/locale/hu.yml +70 -0
- data/lib/ransack/locale/nl.yml +70 -0
- data/lib/ransack/nodes/attribute.rb +2 -2
- data/lib/ransack/nodes/condition.rb +29 -12
- data/lib/ransack/nodes/grouping.rb +6 -6
- data/lib/ransack/nodes/node.rb +1 -1
- data/lib/ransack/nodes/value.rb +1 -1
- data/lib/ransack/predicate.rb +4 -5
- data/lib/ransack/ransacker.rb +1 -1
- data/lib/ransack/search.rb +39 -13
- data/lib/ransack/translate.rb +7 -8
- data/lib/ransack/version.rb +1 -1
- data/ransack.gemspec +5 -5
- data/spec/ransack/adapters/active_record/base_spec.rb +78 -35
- data/spec/ransack/adapters/active_record/context_spec.rb +58 -15
- data/spec/ransack/configuration_spec.rb +18 -18
- data/spec/ransack/dependencies_spec.rb +1 -1
- data/spec/ransack/helpers/form_builder_spec.rb +29 -29
- data/spec/ransack/helpers/form_helper_spec.rb +14 -1
- data/spec/ransack/nodes/condition_spec.rb +21 -2
- data/spec/ransack/predicate_spec.rb +49 -9
- data/spec/ransack/search_spec.rb +178 -143
- data/spec/ransack/translate_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/schema.rb +26 -21
- metadata +15 -11
@@ -0,0 +1,70 @@
|
|
1
|
+
hu:
|
2
|
+
ransack:
|
3
|
+
search: "keresés"
|
4
|
+
predicate: "állítás"
|
5
|
+
and: "és"
|
6
|
+
or: "vagy"
|
7
|
+
any: "bármely"
|
8
|
+
all: "mindegyik"
|
9
|
+
combinator: "combinator"
|
10
|
+
attribute: "attribute"
|
11
|
+
value: "érték"
|
12
|
+
condition: "feltétel"
|
13
|
+
sort: "rendezés"
|
14
|
+
asc: "növekvő"
|
15
|
+
desc: "csökkenő"
|
16
|
+
predicates:
|
17
|
+
eq: "egyenlő"
|
18
|
+
eq_any: "bármelyikkel egyenlő"
|
19
|
+
eq_all: "minddel egyenlő"
|
20
|
+
not_eq: "nem egyenlő"
|
21
|
+
not_eq_any: "nem egyenlő bármelyikkel"
|
22
|
+
not_eq_all: "nem egyenlő egyikkel sem"
|
23
|
+
matches: "egyezik"
|
24
|
+
matches_any: "bármelyikkel egyezik"
|
25
|
+
matches_all: "minddel egyezik"
|
26
|
+
does_not_match: "nem egyezik"
|
27
|
+
does_not_match_any: "nem egyezik semelyikkel"
|
28
|
+
does_not_match_all: "nem egyezik az összessel"
|
29
|
+
lt: "kisebb, mint"
|
30
|
+
lt_any: "bármelyiknél kisebb"
|
31
|
+
lt_all: "mindegyiknél kisebb"
|
32
|
+
lteq: "kisebb vagy egyenlő, mint"
|
33
|
+
lteq_any: "bármelyiknél kisebb vagy egyenlő"
|
34
|
+
lteq_all: "mindegyiknél kisebb vagy egyenlő"
|
35
|
+
gt: "nagyobb, mint"
|
36
|
+
gt_any: "bármelyiknél nagyobb"
|
37
|
+
gt_all: "mindegyiknél nagyobb"
|
38
|
+
gteq: "nagyobb vagy egyenlő, mint"
|
39
|
+
gteq_any: "bármelyiknél nagyobb vagy egyenlő"
|
40
|
+
gteq_all: "mindegyiknél nagyobb vagy egyenlő"
|
41
|
+
in: "értéke"
|
42
|
+
in_any: "értéke bármelyik"
|
43
|
+
in_all: "értéke mindegyik"
|
44
|
+
not_in: "nem ez az értéke"
|
45
|
+
not_in_any: "értéke egyik sem"
|
46
|
+
not_in_all: "értéke nem ezek az elemek"
|
47
|
+
cont: "tartalmazza"
|
48
|
+
cont_any: "bármelyiket tartalmazza"
|
49
|
+
cont_all: "mindet tartalmazza"
|
50
|
+
not_cont: "nem tartalmazza"
|
51
|
+
not_cont_any: "egyiket sem tartalmazza"
|
52
|
+
not_cont_all: "nem tartalmazza mindet"
|
53
|
+
start: "így kezdődik"
|
54
|
+
start_any: "bármelyikkel kezdődik"
|
55
|
+
start_all: "ezekkel kezdődik"
|
56
|
+
not_start: "nem így kezdődik"
|
57
|
+
not_start_any: "nem ezek egyikével kezdődik"
|
58
|
+
not_start_all: "nem ezekkel kezdődik"
|
59
|
+
end: "így végződik"
|
60
|
+
end_any: "bármelyikkel végződik"
|
61
|
+
end_all: "ezekkel végződik"
|
62
|
+
not_end: "nem úgy végződik"
|
63
|
+
not_end_any: "nem ezek egyikével végződik"
|
64
|
+
not_end_all: "nem ezekkel végződik"
|
65
|
+
'true': "igaz"
|
66
|
+
'false': "hamis"
|
67
|
+
present: "létezik"
|
68
|
+
blank: "üres"
|
69
|
+
'null': "null"
|
70
|
+
not_null: "nem null"
|
@@ -0,0 +1,70 @@
|
|
1
|
+
nl:
|
2
|
+
ransack:
|
3
|
+
search: "zoeken"
|
4
|
+
predicate: "eigenschap"
|
5
|
+
and: "en"
|
6
|
+
or: "of"
|
7
|
+
any: "enig"
|
8
|
+
all: "alle"
|
9
|
+
combinator: "combinator"
|
10
|
+
attribute: "attribuut"
|
11
|
+
value: "waarde"
|
12
|
+
condition: "conditie"
|
13
|
+
sort: "sorteren"
|
14
|
+
asc: "oplopend"
|
15
|
+
desc: "aflopend"
|
16
|
+
predicates:
|
17
|
+
eq: "gelijk"
|
18
|
+
eq_any: "gelijk enig"
|
19
|
+
eq_all: "gelijk alle"
|
20
|
+
not_eq: "niet gelijk aan"
|
21
|
+
not_eq_any: "niet gelijk aan enig"
|
22
|
+
not_eq_all: "niet gelijk aan alle"
|
23
|
+
matches: "evenaart"
|
24
|
+
matches_any: "evenaart enig"
|
25
|
+
matches_all: "evenaart alle"
|
26
|
+
does_not_match: "evenaart niet"
|
27
|
+
does_not_match_any: "evenaart niet voor enig"
|
28
|
+
does_not_match_all: "evenaart niet voor alle"
|
29
|
+
lt: "kleiner dan"
|
30
|
+
lt_any: "kleiner dan enig"
|
31
|
+
lt_all: "kleiner dan alle"
|
32
|
+
lteq: "kleiner dan of gelijk aan"
|
33
|
+
lteq_any: "kleiner dan of gelijk aan enig"
|
34
|
+
lteq_all: "kleiner dan of gelijk aan alle"
|
35
|
+
gt: "groter dan"
|
36
|
+
gt_any: "groter dan enig"
|
37
|
+
gt_all: "groter dan alle"
|
38
|
+
gteq: "groter dan or equal to"
|
39
|
+
gteq_any: "groter dan or equal to enig"
|
40
|
+
gteq_all: "groter dan or equal to alle"
|
41
|
+
in: "in"
|
42
|
+
in_any: "in enig"
|
43
|
+
in_all: "in alle"
|
44
|
+
not_in: "niet in"
|
45
|
+
not_in_any: "niet in enig"
|
46
|
+
not_in_all: "niet in alle"
|
47
|
+
cont: "bevat"
|
48
|
+
cont_any: "bevat enig"
|
49
|
+
cont_all: "bevat alle"
|
50
|
+
not_cont: "bevat niet"
|
51
|
+
not_cont_any: "bevat niet enig"
|
52
|
+
not_cont_all: "bevat niet alle"
|
53
|
+
start: "start met"
|
54
|
+
start_any: "start met enig"
|
55
|
+
start_all: "start met alle"
|
56
|
+
not_start: "start niet met"
|
57
|
+
not_start_any: "start niet met enig"
|
58
|
+
not_start_all: "start niet met alle"
|
59
|
+
end: "eindigt met"
|
60
|
+
end_any: "eindigt met enig"
|
61
|
+
end_all: "eindigt met alle"
|
62
|
+
not_end: "eindigt niet met"
|
63
|
+
not_end_any: "eindigt niet met enig"
|
64
|
+
not_end_all: "eindigt niet met alle"
|
65
|
+
'true': "is waar"
|
66
|
+
'false': "is niet waar"
|
67
|
+
present: "is present"
|
68
|
+
blank: "is afwezig"
|
69
|
+
'null': "is null"
|
70
|
+
not_null: "is niet null"
|
@@ -5,8 +5,8 @@ module Ransack
|
|
5
5
|
|
6
6
|
attr_reader :name
|
7
7
|
|
8
|
-
delegate :blank?, :present?, :==, to
|
9
|
-
delegate :engine, to
|
8
|
+
delegate :blank?, :present?, :==, :to => :name
|
9
|
+
delegate :engine, :to => :context
|
10
10
|
|
11
11
|
def initialize(context, name = nil)
|
12
12
|
super(context)
|
@@ -10,20 +10,22 @@ module Ransack
|
|
10
10
|
class << self
|
11
11
|
def extract(context, key, values)
|
12
12
|
attributes, predicate = extract_attributes_and_predicate(key)
|
13
|
-
if attributes.size > 0
|
13
|
+
if attributes.size > 0 && predicate
|
14
14
|
combinator = key.match(/_(or|and)_/) ? $1 : nil
|
15
15
|
condition = self.new(context)
|
16
16
|
condition.build(
|
17
|
-
a
|
18
|
-
p
|
19
|
-
m
|
20
|
-
v
|
17
|
+
:a => attributes,
|
18
|
+
:p => predicate.name,
|
19
|
+
:m => combinator,
|
20
|
+
:v => predicate.wants_array ? Array(values) : [values]
|
21
21
|
)
|
22
22
|
# TODO: Figure out what to do with multiple types of attributes,
|
23
|
-
# if anything.
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
# if anything. Tempted to go with "garbage in, garbage out" here.
|
24
|
+
if predicate.validate(condition.values, condition.default_type)
|
25
|
+
condition
|
26
|
+
else
|
27
|
+
nil
|
28
|
+
end
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
@@ -33,7 +35,9 @@ module Ransack
|
|
33
35
|
str = key.dup
|
34
36
|
name = Predicate.detect_and_strip_from_string!(str)
|
35
37
|
predicate = Predicate.named(name)
|
36
|
-
|
38
|
+
unless predicate || Ransack.options[:ignore_unknown_conditions]
|
39
|
+
raise ArgumentError, "No valid predicate for #{key}"
|
40
|
+
end
|
37
41
|
attributes = str.split(/_and_|_or_/)
|
38
42
|
[attributes, predicate]
|
39
43
|
end
|
@@ -169,8 +173,9 @@ module Ransack
|
|
169
173
|
def arel_predicate
|
170
174
|
predicates = attributes.map do |attr|
|
171
175
|
attr.attr.send(
|
172
|
-
|
173
|
-
)
|
176
|
+
arel_predicate_for_attribute(attr),
|
177
|
+
formatted_values_for_attribute(attr)
|
178
|
+
)
|
174
179
|
end
|
175
180
|
|
176
181
|
if predicates.size > 1
|
@@ -203,6 +208,18 @@ module Ransack
|
|
203
208
|
predicate.wants_array ? formatted : formatted.first
|
204
209
|
end
|
205
210
|
|
211
|
+
def arel_predicate_for_attribute(attr)
|
212
|
+
if predicate.arel_predicate === Proc
|
213
|
+
values = casted_values_for_attribute(attr)
|
214
|
+
predicate.arel_predicate.call(
|
215
|
+
predicate.wants_array ? values : values.first
|
216
|
+
)
|
217
|
+
else
|
218
|
+
predicate.arel_predicate
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
|
206
223
|
def default_type
|
207
224
|
predicate.type || (attributes.first && attributes.first.type)
|
208
225
|
end
|
@@ -9,7 +9,7 @@ module Ransack
|
|
9
9
|
i18n_word :condition, :and, :or
|
10
10
|
i18n_alias :c => :condition, :n => :and, :o => :or
|
11
11
|
|
12
|
-
delegate :each, to
|
12
|
+
delegate :each, :to => :values
|
13
13
|
|
14
14
|
def initialize(context, combinator = nil)
|
15
15
|
super(context)
|
@@ -21,7 +21,9 @@ module Ransack
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def translate(key, options = {})
|
24
|
-
super or Translate.attribute(
|
24
|
+
super or Translate.attribute(
|
25
|
+
key.to_s, options.merge(:context => context)
|
26
|
+
)
|
25
27
|
end
|
26
28
|
|
27
29
|
def conditions
|
@@ -159,10 +161,8 @@ module Ransack
|
|
159
161
|
end
|
160
162
|
|
161
163
|
def inspect
|
162
|
-
data = [
|
163
|
-
|
164
|
-
].
|
165
|
-
reject { |e| e[1].blank? }
|
164
|
+
data = [['conditions', conditions], ['combinator', combinator]]
|
165
|
+
.reject { |e| e[1].blank? }
|
166
166
|
.map { |v| "#{v[0]}: #{v[1]}" }
|
167
167
|
.join(', ')
|
168
168
|
"Grouping <#{data}>"
|
data/lib/ransack/nodes/node.rb
CHANGED
data/lib/ransack/nodes/value.rb
CHANGED
data/lib/ransack/predicate.rb
CHANGED
@@ -45,12 +45,11 @@ module Ransack
|
|
45
45
|
@arel_predicate = opts[:arel_predicate]
|
46
46
|
@type = opts[:type]
|
47
47
|
@formatter = opts[:formatter]
|
48
|
-
@validator = opts[:validator] ||
|
49
|
-
|v| v.respond_to?(:empty?) ? !v.empty? : !v.nil?
|
50
|
-
}
|
48
|
+
@validator = opts[:validator] ||
|
49
|
+
lambda { |v| v.respond_to?(:empty?) ? !v.empty? : !v.nil? }
|
51
50
|
@compound = opts[:compound]
|
52
|
-
@wants_array = opts[:wants_array] == true || @compound ||
|
53
|
-
include?(@arel_predicate)
|
51
|
+
@wants_array = opts[:wants_array] == true || @compound ||
|
52
|
+
['in', 'not_in'].include?(@arel_predicate)
|
54
53
|
end
|
55
54
|
|
56
55
|
def eql?(other)
|
data/lib/ransack/ransacker.rb
CHANGED
data/lib/ransack/search.rb
CHANGED
@@ -8,17 +8,19 @@ module Ransack
|
|
8
8
|
|
9
9
|
attr_reader :base, :context
|
10
10
|
|
11
|
-
delegate :object, :klass, to
|
11
|
+
delegate :object, :klass, :to => :context
|
12
12
|
delegate :new_grouping, :new_condition,
|
13
13
|
:build_grouping, :build_condition,
|
14
|
-
:translate, to
|
14
|
+
:translate, :to => :base
|
15
15
|
|
16
16
|
def initialize(object, params = {}, options = {})
|
17
17
|
params = {} unless params.is_a?(Hash)
|
18
|
-
(params ||= {})
|
19
|
-
|
18
|
+
(params ||= {})
|
19
|
+
.delete_if { |k, v| [*v].all? { |i| i.blank? && i != false } }
|
20
|
+
@context = options[:context] || Context.for(object, options)
|
20
21
|
@context.auth_object = options[:auth_object]
|
21
|
-
@base = Nodes::Grouping.new(@context, 'and')
|
22
|
+
@base = Nodes::Grouping.new(@context, options[:grouping] || 'and')
|
23
|
+
@scope_args = {}
|
22
24
|
build(params.with_indifferent_access)
|
23
25
|
end
|
24
26
|
|
@@ -28,11 +30,14 @@ module Ransack
|
|
28
30
|
|
29
31
|
def build(params)
|
30
32
|
collapse_multiparameter_attributes!(params).each do |key, value|
|
31
|
-
|
32
|
-
when 's', 'sorts'
|
33
|
+
if ['s', 'sorts'].include?(key)
|
33
34
|
send("#{key}=", value)
|
34
|
-
|
35
|
-
base.send("#{key}=", value)
|
35
|
+
elsif base.attribute_method?(key)
|
36
|
+
base.send("#{key}=", value)
|
37
|
+
elsif @context.ransackable_scope?(key, @context.object)
|
38
|
+
add_scope(key, value)
|
39
|
+
elsif !Ransack.options[:ignore_unknown_conditions]
|
40
|
+
raise ArgumentError, "Invalid search term #{key}"
|
36
41
|
end
|
37
42
|
end
|
38
43
|
self
|
@@ -57,7 +62,8 @@ module Ransack
|
|
57
62
|
when String
|
58
63
|
self.sorts = [args]
|
59
64
|
else
|
60
|
-
raise ArgumentError,
|
65
|
+
raise ArgumentError,
|
66
|
+
"Invalid argument (#{args.class}) supplied to sorts="
|
61
67
|
end
|
62
68
|
end
|
63
69
|
alias :s= :sorts=
|
@@ -79,20 +85,40 @@ module Ransack
|
|
79
85
|
|
80
86
|
def method_missing(method_id, *args)
|
81
87
|
method_name = method_id.to_s
|
82
|
-
|
83
|
-
if base.attribute_method?(
|
88
|
+
getter_name = method_name.sub(/=$/, '')
|
89
|
+
if base.attribute_method?(getter_name)
|
84
90
|
base.send(method_id, *args)
|
91
|
+
elsif @context.ransackable_scope?(getter_name, @context.object)
|
92
|
+
if method_name =~ /=$/
|
93
|
+
add_scope getter_name, args
|
94
|
+
else
|
95
|
+
@scope_args[method_name]
|
96
|
+
end
|
85
97
|
else
|
86
98
|
super
|
87
99
|
end
|
88
100
|
end
|
89
101
|
|
90
102
|
def inspect
|
91
|
-
|
103
|
+
details = [
|
104
|
+
[:class, klass.name],
|
105
|
+
([:scope, @scope_args] if @scope_args.present?),
|
106
|
+
[:base, base.inspect]
|
107
|
+
].compact.map { |d| d.join(': ') }.join(', ')
|
108
|
+
"Ransack::Search<#{details}>"
|
92
109
|
end
|
93
110
|
|
94
111
|
private
|
95
112
|
|
113
|
+
def add_scope(key, args)
|
114
|
+
if @context.scope_arity(key) == 1
|
115
|
+
@scope_args[key] = args.is_a?(Array) ? args[0] : args
|
116
|
+
else
|
117
|
+
@scope_args[key] = args
|
118
|
+
end
|
119
|
+
@context.chain_scope(key, args)
|
120
|
+
end
|
121
|
+
|
96
122
|
def collapse_multiparameter_attributes!(attrs)
|
97
123
|
attrs.keys.each do |k|
|
98
124
|
if k.include?("(")
|
data/lib/ransack/translate.rb
CHANGED
@@ -5,11 +5,11 @@ I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'locale', '*.yml')]
|
|
5
5
|
module Ransack
|
6
6
|
module Translate
|
7
7
|
def self.word(key, options = {})
|
8
|
-
I18n.translate(:"ransack.#{key}", default
|
8
|
+
I18n.translate(:"ransack.#{key}", :default => key.to_s)
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.predicate(key, options = {})
|
12
|
-
I18n.translate(:"ransack.predicates.#{key}", default
|
12
|
+
I18n.translate(:"ransack.predicates.#{key}", :default => key.to_s)
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.attribute(key, options = {})
|
@@ -47,7 +47,7 @@ module Ransack
|
|
47
47
|
end
|
48
48
|
|
49
49
|
defaults << options.delete(:default) if options[:default]
|
50
|
-
options.reverse_merge! count
|
50
|
+
options.reverse_merge! :count => 1, :default => defaults
|
51
51
|
I18n.translate(defaults.shift, options.merge(interpolations))
|
52
52
|
end
|
53
53
|
|
@@ -60,7 +60,7 @@ module Ransack
|
|
60
60
|
[:"#{context.klass.i18n_scope}.models.#{i18n_key(context.klass)}"] :
|
61
61
|
[:"ransack.associations.#{i18n_key(context.klass)}.#{key}"]
|
62
62
|
defaults << context.traverse(key).model_name.human
|
63
|
-
options = { count
|
63
|
+
options = { :count => 1, :default => defaults }
|
64
64
|
I18n.translate(defaults.shift, options)
|
65
65
|
end
|
66
66
|
|
@@ -75,7 +75,7 @@ module Ransack
|
|
75
75
|
:"ransack.attributes.#{
|
76
76
|
i18n_key(associated_class || context.klass)
|
77
77
|
}.#{attr_name}",
|
78
|
-
default
|
78
|
+
:default => [
|
79
79
|
(
|
80
80
|
if associated_class
|
81
81
|
:"#{associated_class.i18n_scope}.attributes.#{
|
@@ -93,13 +93,12 @@ module Ransack
|
|
93
93
|
if include_associations && associated_class
|
94
94
|
defaults << '%{association_name} %{attr_fallback_name}'
|
95
95
|
interpolations[:association_name] = association(
|
96
|
-
assoc_path,
|
97
|
-
context: context
|
96
|
+
assoc_path, :context => context
|
98
97
|
)
|
99
98
|
else
|
100
99
|
defaults << '%{attr_fallback_name}'
|
101
100
|
end
|
102
|
-
options = { count
|
101
|
+
options = { :count => 1, :default => defaults }
|
103
102
|
I18n.translate(defaults.shift, options.merge(interpolations))
|
104
103
|
end
|
105
104
|
|