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.
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