ransack 1.1.0 → 1.2.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 +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
|