ransack 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +12 -4
  3. data/CONTRIBUTING.md +10 -4
  4. data/Gemfile +12 -9
  5. data/README.md +46 -11
  6. data/lib/ransack.rb +4 -2
  7. data/lib/ransack/adapters/active_record.rb +1 -1
  8. data/lib/ransack/adapters/active_record/3.0/compat.rb +16 -6
  9. data/lib/ransack/adapters/active_record/3.0/context.rb +32 -16
  10. data/lib/ransack/adapters/active_record/3.1/context.rb +32 -15
  11. data/lib/ransack/adapters/active_record/3.2/context.rb +1 -1
  12. data/lib/ransack/adapters/active_record/base.rb +9 -6
  13. data/lib/ransack/adapters/active_record/context.rb +193 -2
  14. data/lib/ransack/configuration.rb +4 -4
  15. data/lib/ransack/constants.rb +81 -18
  16. data/lib/ransack/context.rb +27 -12
  17. data/lib/ransack/helpers/form_builder.rb +126 -91
  18. data/lib/ransack/helpers/form_helper.rb +34 -12
  19. data/lib/ransack/naming.rb +2 -1
  20. data/lib/ransack/nodes/attribute.rb +6 -4
  21. data/lib/ransack/nodes/bindable.rb +3 -1
  22. data/lib/ransack/nodes/condition.rb +40 -27
  23. data/lib/ransack/nodes/grouping.rb +19 -13
  24. data/lib/ransack/nodes/node.rb +3 -3
  25. data/lib/ransack/nodes/sort.rb +5 -3
  26. data/lib/ransack/nodes/value.rb +2 -2
  27. data/lib/ransack/predicate.rb +18 -9
  28. data/lib/ransack/ransacker.rb +4 -4
  29. data/lib/ransack/search.rb +9 -12
  30. data/lib/ransack/translate.rb +42 -21
  31. data/lib/ransack/version.rb +1 -1
  32. data/lib/ransack/visitor.rb +4 -4
  33. data/ransack.gemspec +17 -7
  34. data/spec/blueprints/notes.rb +2 -0
  35. data/spec/blueprints/people.rb +4 -1
  36. data/spec/console.rb +3 -3
  37. data/spec/ransack/adapters/active_record/base_spec.rb +149 -22
  38. data/spec/ransack/adapters/active_record/context_spec.rb +5 -5
  39. data/spec/ransack/configuration_spec.rb +17 -8
  40. data/spec/ransack/dependencies_spec.rb +8 -0
  41. data/spec/ransack/helpers/form_builder_spec.rb +37 -14
  42. data/spec/ransack/helpers/form_helper_spec.rb +5 -5
  43. data/spec/ransack/predicate_spec.rb +6 -3
  44. data/spec/ransack/search_spec.rb +95 -73
  45. data/spec/ransack/translate_spec.rb +14 -0
  46. data/spec/spec_helper.rb +14 -8
  47. data/spec/support/en.yml +6 -0
  48. data/spec/support/schema.rb +76 -31
  49. 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, :to => :values
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(:context => context))
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, "Invalid argument (#{groupings.class}) supplied to groupings="
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 ? write_attribute(method_name, *args) : read_attribute(method_name)
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_/).select {|n| !@context.attribute_method?(n)}.empty?
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 =[['conditions', conditions], ['combinator', combinator]].reject { |e|
159
- e[1].blank?
160
- }.map { |v| "#{v[0]}: #{v[1]}" }.join(', ')
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
@@ -2,7 +2,7 @@ module Ransack
2
2
  module Nodes
3
3
  class Node
4
4
  attr_reader :context
5
- delegate :contextualize, :to => :context
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
@@ -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(:name => attr, :dir => direction)
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
@@ -2,7 +2,7 @@ module Ransack
2
2
  module Nodes
3
3
  class Value < Node
4
4
  attr_accessor :value
5
- delegate :present?, :blank?, :to => :value
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
@@ -1,6 +1,7 @@
1
1
  module Ransack
2
2
  class Predicate
3
- attr_reader :name, :arel_predicate, :type, :formatter, :validator, :compound, :wants_array
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
- names_by_decreasing_length.detect {|p| str.sub!(/_#{p}$/, '')}
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.match(/_#{p}$/)}
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 {|p| attribute_name.to_s.match(/_#{p}$/)}
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 { |v| v.respond_to?(:empty?) ? !v.empty? : !v.nil? }
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'].include?(@arel_predicate)
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
@@ -3,7 +3,7 @@ module Ransack
3
3
 
4
4
  attr_reader :name, :type, :formatter, :args
5
5
 
6
- delegate :call, :to => :@callable
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
@@ -8,13 +8,14 @@ module Ransack
8
8
 
9
9
  attr_reader :base, :context
10
10
 
11
- delegate :object, :klass, :to => :context
11
+ delegate :object, :klass, to: :context
12
12
  delegate :new_grouping, :new_condition,
13
13
  :build_grouping, :build_condition,
14
- :translate, :to => :base
14
+ :translate, to: :base
15
15
 
16
16
  def initialize(object, params = {}, options = {})
17
- (params ||= {}).delete_if { |k, v| v.blank? && v != false }
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 = Nodes::Sort.extract(@context, 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!(/\=$/, '')
@@ -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}", :default => key.to_s)
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}", :default => key.to_s)
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 { |x| x.respond_to?(:model_name) }
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.model_name.singular}.#{original_name}"
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(" #{Translate.word(combinator)} ")
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! :count => 1, :default => defaults
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? ? [:"#{context.klass.i18n_scope}.models.#{context.klass.model_name.singular}"] : [:"ransack.associations.#{context.klass.model_name.singular}.#{key}"]
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 = {:count => 1, :default => defaults}
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
- (associated_class ?
68
- :"ransack.attributes.#{associated_class.model_name.singular}.#{attr_name}" :
69
- :"ransack.attributes.#{context.klass.model_name.singular}.#{attr_name}"
70
- ),
71
- :default => [
72
- (associated_class ?
73
- :"#{associated_class.i18n_scope}.attributes.#{associated_class.model_name.singular}.#{attr_name}" :
74
- :"#{context.klass.i18n_scope}.attributes.#{context.klass.model_name.singular}.#{attr_name}"
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(assoc_path, :context => context)
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 = {:count => 1, :default => defaults}
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
@@ -1,3 +1,3 @@
1
1
  module Ransack
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -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