ransack 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +12 -4
- data/CONTRIBUTING.md +10 -4
- data/Gemfile +12 -9
- data/README.md +46 -11
- data/lib/ransack.rb +4 -2
- data/lib/ransack/adapters/active_record.rb +1 -1
- data/lib/ransack/adapters/active_record/3.0/compat.rb +16 -6
- data/lib/ransack/adapters/active_record/3.0/context.rb +32 -16
- data/lib/ransack/adapters/active_record/3.1/context.rb +32 -15
- data/lib/ransack/adapters/active_record/3.2/context.rb +1 -1
- data/lib/ransack/adapters/active_record/base.rb +9 -6
- data/lib/ransack/adapters/active_record/context.rb +193 -2
- data/lib/ransack/configuration.rb +4 -4
- data/lib/ransack/constants.rb +81 -18
- data/lib/ransack/context.rb +27 -12
- data/lib/ransack/helpers/form_builder.rb +126 -91
- data/lib/ransack/helpers/form_helper.rb +34 -12
- data/lib/ransack/naming.rb +2 -1
- data/lib/ransack/nodes/attribute.rb +6 -4
- data/lib/ransack/nodes/bindable.rb +3 -1
- data/lib/ransack/nodes/condition.rb +40 -27
- data/lib/ransack/nodes/grouping.rb +19 -13
- data/lib/ransack/nodes/node.rb +3 -3
- data/lib/ransack/nodes/sort.rb +5 -3
- data/lib/ransack/nodes/value.rb +2 -2
- data/lib/ransack/predicate.rb +18 -9
- data/lib/ransack/ransacker.rb +4 -4
- data/lib/ransack/search.rb +9 -12
- data/lib/ransack/translate.rb +42 -21
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +4 -4
- data/ransack.gemspec +17 -7
- data/spec/blueprints/notes.rb +2 -0
- data/spec/blueprints/people.rb +4 -1
- data/spec/console.rb +3 -3
- data/spec/ransack/adapters/active_record/base_spec.rb +149 -22
- data/spec/ransack/adapters/active_record/context_spec.rb +5 -5
- data/spec/ransack/configuration_spec.rb +17 -8
- data/spec/ransack/dependencies_spec.rb +8 -0
- data/spec/ransack/helpers/form_builder_spec.rb +37 -14
- data/spec/ransack/helpers/form_helper_spec.rb +5 -5
- data/spec/ransack/predicate_spec.rb +6 -3
- data/spec/ransack/search_spec.rb +95 -73
- data/spec/ransack/translate_spec.rb +14 -0
- data/spec/spec_helper.rb +14 -8
- data/spec/support/en.yml +6 -0
- data/spec/support/schema.rb +76 -31
- metadata +48 -29
@@ -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, :
|
12
|
+
delegate :each, to: :values
|
13
13
|
|
14
14
|
def initialize(context, combinator = nil)
|
15
15
|
super(context)
|
@@ -21,7 +21,7 @@ module Ransack
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def translate(key, options = {})
|
24
|
-
super or Translate.attribute(key.to_s, options.merge(:
|
24
|
+
super or Translate.attribute(key.to_s, options.merge(context: context))
|
25
25
|
end
|
26
26
|
|
27
27
|
def conditions
|
@@ -48,7 +48,7 @@ module Ransack
|
|
48
48
|
alias :c= :conditions=
|
49
49
|
|
50
50
|
def [](key)
|
51
|
-
if condition = conditions.detect {|c| c.key == key.to_s}
|
51
|
+
if condition = conditions.detect { |c| c.key == key.to_s }
|
52
52
|
condition
|
53
53
|
else
|
54
54
|
nil
|
@@ -56,7 +56,7 @@ module Ransack
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def []=(key, value)
|
59
|
-
conditions.reject! {|c| c.key == key.to_s}
|
59
|
+
conditions.reject! { |c| c.key == key.to_s }
|
60
60
|
self.conditions << value
|
61
61
|
end
|
62
62
|
|
@@ -82,7 +82,6 @@ module Ransack
|
|
82
82
|
attrs = opts[:attributes] || 1
|
83
83
|
vals = opts[:values] || 1
|
84
84
|
condition = Condition.new(@context)
|
85
|
-
condition.predicate = Predicate.named('eq')
|
86
85
|
attrs.times { condition.build_attribute }
|
87
86
|
vals.times { condition.build_value }
|
88
87
|
condition
|
@@ -106,7 +105,8 @@ module Ransack
|
|
106
105
|
self.groupings << grouping_object if grouping_object.values.any?
|
107
106
|
end
|
108
107
|
else
|
109
|
-
raise ArgumentError,
|
108
|
+
raise ArgumentError,
|
109
|
+
"Invalid argument (#{groupings.class}) supplied to groupings="
|
110
110
|
end
|
111
111
|
end
|
112
112
|
alias :g= :groupings=
|
@@ -115,7 +115,9 @@ module Ransack
|
|
115
115
|
method_name = method_id.to_s
|
116
116
|
writer = method_name.sub!(/\=$/, '')
|
117
117
|
if attribute_method?(method_name)
|
118
|
-
writer ?
|
118
|
+
writer ?
|
119
|
+
write_attribute(method_name, *args) :
|
120
|
+
read_attribute(method_name)
|
119
121
|
else
|
120
122
|
super
|
121
123
|
end
|
@@ -127,7 +129,9 @@ module Ransack
|
|
127
129
|
when /^(g|c|m|groupings|conditions|combinator)=?$/
|
128
130
|
true
|
129
131
|
else
|
130
|
-
name.split(/_and_|_or_/)
|
132
|
+
name.split(/_and_|_or_/)
|
133
|
+
.select { |n| !@context.attribute_method?(n) }
|
134
|
+
.empty?
|
131
135
|
end
|
132
136
|
end
|
133
137
|
|
@@ -155,9 +159,12 @@ module Ransack
|
|
155
159
|
end
|
156
160
|
|
157
161
|
def inspect
|
158
|
-
data =
|
159
|
-
|
160
|
-
|
162
|
+
data = [
|
163
|
+
['conditions', conditions], ['combinator', combinator]
|
164
|
+
].
|
165
|
+
reject { |e| e[1].blank? }
|
166
|
+
.map { |v| "#{v[0]}: #{v[1]}" }
|
167
|
+
.join(', ')
|
161
168
|
"Grouping <#{data}>"
|
162
169
|
end
|
163
170
|
|
@@ -183,7 +190,6 @@ module Ransack
|
|
183
190
|
Predicate.detect_and_strip_from_string!(string)
|
184
191
|
string
|
185
192
|
end
|
186
|
-
|
187
193
|
end
|
188
194
|
end
|
189
|
-
end
|
195
|
+
end
|
data/lib/ransack/nodes/node.rb
CHANGED
@@ -2,7 +2,7 @@ module Ransack
|
|
2
2
|
module Nodes
|
3
3
|
class Node
|
4
4
|
attr_reader :context
|
5
|
-
delegate :contextualize, :
|
5
|
+
delegate :contextualize, to: :context
|
6
6
|
class_attribute :i18n_words
|
7
7
|
class_attribute :i18n_aliases
|
8
8
|
self.i18n_words = []
|
@@ -14,7 +14,7 @@ module Ransack
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def i18n_alias(opts = {})
|
17
|
-
self.i18n_aliases.merge! Hash[opts.map {|k, v| [k.to_s, v.to_s]}]
|
17
|
+
self.i18n_aliases.merge! Hash[opts.map { |k, v| [k.to_s, v.to_s] }]
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -31,4 +31,4 @@ module Ransack
|
|
31
31
|
|
32
32
|
end
|
33
33
|
end
|
34
|
-
end
|
34
|
+
end
|
data/lib/ransack/nodes/sort.rb
CHANGED
@@ -9,7 +9,7 @@ module Ransack
|
|
9
9
|
class << self
|
10
10
|
def extract(context, str)
|
11
11
|
attr, direction = str.split(/\s+/,2)
|
12
|
-
self.new(context).build(:
|
12
|
+
self.new(context).build(name: attr, dir: direction)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -24,7 +24,9 @@ module Ransack
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def valid?
|
27
|
-
bound? && attr
|
27
|
+
bound? && attr &&
|
28
|
+
context.klassify(parent).ransortable_attributes(context.auth_object)
|
29
|
+
.include?(attr_name)
|
28
30
|
end
|
29
31
|
|
30
32
|
def name=(name)
|
@@ -39,4 +41,4 @@ module Ransack
|
|
39
41
|
|
40
42
|
end
|
41
43
|
end
|
42
|
-
end
|
44
|
+
end
|
data/lib/ransack/nodes/value.rb
CHANGED
@@ -2,7 +2,7 @@ module Ransack
|
|
2
2
|
module Nodes
|
3
3
|
class Value < Node
|
4
4
|
attr_accessor :value
|
5
|
-
delegate :present?, :blank?, :
|
5
|
+
delegate :present?, :blank?, to: :value
|
6
6
|
|
7
7
|
def initialize(context, value = nil)
|
8
8
|
super(context)
|
@@ -107,4 +107,4 @@ module Ransack
|
|
107
107
|
end
|
108
108
|
end
|
109
109
|
end
|
110
|
-
end
|
110
|
+
end
|
data/lib/ransack/predicate.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Ransack
|
2
2
|
class Predicate
|
3
|
-
attr_reader :name, :arel_predicate, :type, :formatter, :validator,
|
3
|
+
attr_reader :name, :arel_predicate, :type, :formatter, :validator,
|
4
|
+
:compound, :wants_array
|
4
5
|
|
5
6
|
class << self
|
6
7
|
|
@@ -9,7 +10,7 @@ module Ransack
|
|
9
10
|
end
|
10
11
|
|
11
12
|
def names_by_decreasing_length
|
12
|
-
names.sort {|a,b| b.length <=> a.length}
|
13
|
+
names.sort { |a,b| b.length <=> a.length }
|
13
14
|
end
|
14
15
|
|
15
16
|
def named(name)
|
@@ -17,15 +18,20 @@ module Ransack
|
|
17
18
|
end
|
18
19
|
|
19
20
|
def detect_and_strip_from_string!(str)
|
20
|
-
|
21
|
+
if p = detect_from_string(str)
|
22
|
+
str.sub! /_#{p}$/, ''
|
23
|
+
p
|
24
|
+
end
|
21
25
|
end
|
22
26
|
|
23
27
|
def detect_from_string(str)
|
24
|
-
names_by_decreasing_length.detect {|p| str.
|
28
|
+
names_by_decreasing_length.detect { |p| str.end_with?("_#{p}") }
|
25
29
|
end
|
26
30
|
|
27
31
|
def name_from_attribute_name(attribute_name)
|
28
|
-
names_by_decreasing_length.detect {
|
32
|
+
names_by_decreasing_length.detect {
|
33
|
+
|p| attribute_name.to_s.match(/_#{p}$/)
|
34
|
+
}
|
29
35
|
end
|
30
36
|
|
31
37
|
def for_attribute_name(attribute_name)
|
@@ -39,9 +45,12 @@ module Ransack
|
|
39
45
|
@arel_predicate = opts[:arel_predicate]
|
40
46
|
@type = opts[:type]
|
41
47
|
@formatter = opts[:formatter]
|
42
|
-
@validator = opts[:validator] || lambda {
|
48
|
+
@validator = opts[:validator] || lambda {
|
49
|
+
|v| v.respond_to?(:empty?) ? !v.empty? : !v.nil?
|
50
|
+
}
|
43
51
|
@compound = opts[:compound]
|
44
|
-
@wants_array = opts[:wants_array] == true || @compound || ['in', 'not_in'].
|
52
|
+
@wants_array = opts[:wants_array] == true || @compound || ['in', 'not_in'].
|
53
|
+
include?(@arel_predicate)
|
45
54
|
end
|
46
55
|
|
47
56
|
def eql?(other)
|
@@ -63,8 +72,8 @@ module Ransack
|
|
63
72
|
end
|
64
73
|
|
65
74
|
def validate(vals, type = @type)
|
66
|
-
vals.select {|v| validator.call(type ? v.cast(type) : v.value)}.any?
|
75
|
+
vals.select { |v| validator.call(type ? v.cast(type) : v.value) }.any?
|
67
76
|
end
|
68
77
|
|
69
78
|
end
|
70
|
-
end
|
79
|
+
end
|
data/lib/ransack/ransacker.rb
CHANGED
@@ -3,7 +3,7 @@ module Ransack
|
|
3
3
|
|
4
4
|
attr_reader :name, :type, :formatter, :args
|
5
5
|
|
6
|
-
delegate :call, :
|
6
|
+
delegate :call, to: :@callable
|
7
7
|
|
8
8
|
def initialize(klass, name, opts = {}, &block)
|
9
9
|
@klass, @name = klass, name
|
@@ -13,12 +13,12 @@ module Ransack
|
|
13
13
|
@formatter = opts[:formatter]
|
14
14
|
@callable = opts[:callable] || block ||
|
15
15
|
(@klass.method(name) if @klass.respond_to?(name)) ||
|
16
|
-
proc {|parent| parent.table[name]}
|
16
|
+
proc { |parent| parent.table[name] }
|
17
17
|
end
|
18
18
|
|
19
19
|
def attr_from(bindable)
|
20
|
-
call(*args.map {|arg| bindable.send(arg)})
|
20
|
+
call(*args.map { |arg| bindable.send(arg) })
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
24
|
-
end
|
24
|
+
end
|
data/lib/ransack/search.rb
CHANGED
@@ -8,13 +8,14 @@ module Ransack
|
|
8
8
|
|
9
9
|
attr_reader :base, :context
|
10
10
|
|
11
|
-
delegate :object, :klass, :
|
11
|
+
delegate :object, :klass, to: :context
|
12
12
|
delegate :new_grouping, :new_condition,
|
13
13
|
:build_grouping, :build_condition,
|
14
|
-
:translate, :
|
14
|
+
:translate, to: :base
|
15
15
|
|
16
16
|
def initialize(object, params = {}, options = {})
|
17
|
-
|
17
|
+
params = {} unless params.is_a?(Hash)
|
18
|
+
params.delete_if { |k, v| v.blank? && v != false }
|
18
19
|
@context = Context.for(object, options)
|
19
20
|
@context.auth_object = options[:auth_object]
|
20
21
|
@base = Nodes::Grouping.new(@context, 'and')
|
@@ -41,7 +42,11 @@ module Ransack
|
|
41
42
|
case args
|
42
43
|
when Array
|
43
44
|
args.each do |sort|
|
44
|
-
sort
|
45
|
+
if sort.kind_of? Hash
|
46
|
+
sort = Nodes::Sort.new(@context).build(sort)
|
47
|
+
else
|
48
|
+
sort = Nodes::Sort.extract(@context, sort)
|
49
|
+
end
|
45
50
|
self.sorts << sort
|
46
51
|
end
|
47
52
|
when Hash
|
@@ -72,14 +77,6 @@ module Ransack
|
|
72
77
|
Nodes::Sort.new(@context).build(opts)
|
73
78
|
end
|
74
79
|
|
75
|
-
def respond_to?(method_id, include_private = false)
|
76
|
-
super or begin
|
77
|
-
method_name = method_id.to_s
|
78
|
-
writer = method_name.sub!(/\=$/, '')
|
79
|
-
base.attribute_method?(method_name) ? true : false
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
80
|
def method_missing(method_id, *args)
|
84
81
|
method_name = method_id.to_s
|
85
82
|
writer = method_name.sub!(/\=$/, '')
|
data/lib/ransack/translate.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
|
1
3
|
I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'locale', '*.yml')]
|
2
4
|
|
3
5
|
module Ransack
|
4
6
|
module Translate
|
5
7
|
def self.word(key, options = {})
|
6
|
-
I18n.translate(:"ransack.#{key}", :
|
8
|
+
I18n.translate(:"ransack.#{key}", default: key.to_s)
|
7
9
|
end
|
8
10
|
|
9
11
|
def self.predicate(key, options = {})
|
10
|
-
I18n.translate(:"ransack.predicates.#{key}", :
|
12
|
+
I18n.translate(:"ransack.predicates.#{key}", default: key.to_s)
|
11
13
|
end
|
12
14
|
|
13
15
|
def self.attribute(key, options = {})
|
@@ -17,13 +19,15 @@ module Ransack
|
|
17
19
|
|
18
20
|
original_name = key.to_s
|
19
21
|
base_class = context.klass
|
20
|
-
base_ancestors = base_class.ancestors.select {
|
22
|
+
base_ancestors = base_class.ancestors.select {
|
23
|
+
|x| x.respond_to?(:model_name)
|
24
|
+
}
|
21
25
|
predicate = Predicate.detect_from_string(original_name)
|
22
26
|
attributes_str = original_name.sub(/_#{predicate}$/, '')
|
23
27
|
attribute_names = attributes_str.split(/_and_|_or_/)
|
24
28
|
combinator = attributes_str.match(/_and_/) ? :and : :or
|
25
29
|
defaults = base_ancestors.map do |klass|
|
26
|
-
:"ransack.attributes.#{klass
|
30
|
+
:"ransack.attributes.#{i18n_key(klass)}.#{original_name}"
|
27
31
|
end
|
28
32
|
|
29
33
|
translated_names = attribute_names.map do |attr|
|
@@ -31,7 +35,9 @@ module Ransack
|
|
31
35
|
end
|
32
36
|
|
33
37
|
interpolations = {}
|
34
|
-
interpolations[:attributes] = translated_names.join(
|
38
|
+
interpolations[:attributes] = translated_names.join(
|
39
|
+
" #{Translate.word(combinator)} "
|
40
|
+
)
|
35
41
|
|
36
42
|
if predicate
|
37
43
|
defaults << "%{attributes} %{predicate}"
|
@@ -41,7 +47,7 @@ module Ransack
|
|
41
47
|
end
|
42
48
|
|
43
49
|
defaults << options.delete(:default) if options[:default]
|
44
|
-
options.reverse_merge! :
|
50
|
+
options.reverse_merge! count: 1, default: defaults
|
45
51
|
I18n.translate(defaults.shift, options.merge(interpolations))
|
46
52
|
end
|
47
53
|
|
@@ -50,9 +56,11 @@ module Ransack
|
|
50
56
|
raise ArgumentError, "A context is required to translate associations"
|
51
57
|
end
|
52
58
|
|
53
|
-
defaults = key.blank? ?
|
59
|
+
defaults = key.blank? ?
|
60
|
+
[:"#{context.klass.i18n_scope}.models.#{i18n_key(context.klass)}"] :
|
61
|
+
[:"ransack.associations.#{i18n_key(context.klass)}.#{key}"]
|
54
62
|
defaults << context.traverse(key).model_name.human
|
55
|
-
options = {:
|
63
|
+
options = { count: 1, default: defaults }
|
56
64
|
I18n.translate(defaults.shift, options)
|
57
65
|
end
|
58
66
|
|
@@ -64,30 +72,43 @@ module Ransack
|
|
64
72
|
attr_name = name.sub(/^#{assoc_path}_/, '')
|
65
73
|
interpolations = {}
|
66
74
|
interpolations[:attr_fallback_name] = I18n.translate(
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
+
:"ransack.attributes.#{
|
76
|
+
i18n_key(associated_class || context.klass)
|
77
|
+
}.#{attr_name}",
|
78
|
+
default: [
|
79
|
+
(
|
80
|
+
if associated_class
|
81
|
+
:"#{associated_class.i18n_scope}.attributes.#{
|
82
|
+
i18n_key(associated_class)}.#{attr_name}"
|
83
|
+
else
|
84
|
+
:"#{context.klass.i18n_scope}.attributes.#{
|
85
|
+
i18n_key(context.klass)}.#{attr_name}"
|
86
|
+
end
|
75
87
|
),
|
76
88
|
:".attributes.#{attr_name}",
|
77
89
|
attr_name.humanize
|
78
90
|
]
|
79
91
|
)
|
80
|
-
defaults = [
|
81
|
-
:"ransack.attributes.#{context.klass.model_name.singular}.#{name}"
|
82
|
-
]
|
92
|
+
defaults = [:"ransack.attributes.#{i18n_key(context.klass)}.#{name}"]
|
83
93
|
if include_associations && associated_class
|
84
94
|
defaults << '%{association_name} %{attr_fallback_name}'
|
85
|
-
interpolations[:association_name] = association(
|
95
|
+
interpolations[:association_name] = association(
|
96
|
+
assoc_path,
|
97
|
+
context: context
|
98
|
+
)
|
86
99
|
else
|
87
100
|
defaults << '%{attr_fallback_name}'
|
88
101
|
end
|
89
|
-
options = {:
|
102
|
+
options = { count: 1, default: defaults }
|
90
103
|
I18n.translate(defaults.shift, options.merge(interpolations))
|
91
104
|
end
|
105
|
+
|
106
|
+
def self.i18n_key(klass)
|
107
|
+
if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
108
|
+
klass.model_name.i18n_key.to_s.tr('.', '/')
|
109
|
+
else
|
110
|
+
klass.model_name.i18n_key.to_s
|
111
|
+
end
|
112
|
+
end
|
92
113
|
end
|
93
114
|
end
|
data/lib/ransack/version.rb
CHANGED
data/lib/ransack/visitor.rb
CHANGED
@@ -10,7 +10,7 @@ module Ransack
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def visit_Array(object)
|
13
|
-
object.map {|o| accept(o)}.compact
|
13
|
+
object.map { |o| accept(o) }.compact
|
14
14
|
end
|
15
15
|
|
16
16
|
def visit_Ransack_Nodes_Condition(object)
|
@@ -22,7 +22,7 @@ module Ransack
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def visit_and(object)
|
25
|
-
nodes = object.values.map {|o| accept(o)}.compact
|
25
|
+
nodes = object.values.map { |o| accept(o) }.compact
|
26
26
|
return nil unless nodes.size > 0
|
27
27
|
|
28
28
|
if nodes.size > 1
|
@@ -33,7 +33,7 @@ module Ransack
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def visit_or(object)
|
36
|
-
nodes = object.values.map {|o| accept(o)}.compact
|
36
|
+
nodes = object.values.map { |o| accept(o) }.compact
|
37
37
|
return nil unless nodes.size > 0
|
38
38
|
|
39
39
|
if nodes.size > 1
|
@@ -65,4 +65,4 @@ module Ransack
|
|
65
65
|
end
|
66
66
|
|
67
67
|
end
|
68
|
-
end
|
68
|
+
end
|