ransack 1.5.1 → 1.6.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 +47 -3
- data/CHANGELOG.md +106 -18
- data/CONTRIBUTING.md +56 -23
- data/Gemfile +16 -5
- data/README.md +114 -38
- data/Rakefile +30 -2
- data/lib/ransack.rb +9 -0
- data/lib/ransack/adapters/active_record/3.0/compat.rb +11 -8
- data/lib/ransack/adapters/active_record/3.0/context.rb +14 -22
- data/lib/ransack/adapters/active_record/3.1/context.rb +14 -22
- data/lib/ransack/adapters/active_record/context.rb +36 -31
- data/lib/ransack/adapters/active_record/ransack/constants.rb +113 -0
- data/lib/ransack/adapters/active_record/ransack/context.rb +64 -0
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +48 -0
- data/lib/ransack/adapters/active_record/ransack/translate.rb +12 -0
- data/lib/ransack/adapters/active_record/ransack/visitor.rb +24 -0
- data/lib/ransack/adapters/mongoid.rb +13 -0
- data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
- data/lib/ransack/adapters/mongoid/attributes/attribute.rb +37 -0
- data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +17 -0
- data/lib/ransack/adapters/mongoid/attributes/predications.rb +141 -0
- data/lib/ransack/adapters/mongoid/base.rb +126 -0
- data/lib/ransack/adapters/mongoid/context.rb +208 -0
- data/lib/ransack/adapters/mongoid/inquiry_hash.rb +23 -0
- data/lib/ransack/adapters/mongoid/ransack/constants.rb +88 -0
- data/lib/ransack/adapters/mongoid/ransack/context.rb +60 -0
- data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +27 -0
- data/lib/ransack/adapters/mongoid/ransack/translate.rb +13 -0
- data/lib/ransack/adapters/mongoid/ransack/visitor.rb +24 -0
- data/lib/ransack/adapters/mongoid/table.rb +35 -0
- data/lib/ransack/configuration.rb +22 -4
- data/lib/ransack/constants.rb +26 -120
- data/lib/ransack/context.rb +32 -60
- data/lib/ransack/helpers/form_builder.rb +50 -36
- data/lib/ransack/helpers/form_helper.rb +148 -104
- data/lib/ransack/naming.rb +11 -11
- data/lib/ransack/nodes.rb +2 -0
- data/lib/ransack/nodes/bindable.rb +12 -4
- data/lib/ransack/nodes/condition.rb +5 -22
- data/lib/ransack/nodes/grouping.rb +9 -10
- data/lib/ransack/nodes/sort.rb +3 -2
- data/lib/ransack/nodes/value.rb +1 -2
- data/lib/ransack/predicate.rb +3 -3
- data/lib/ransack/search.rb +46 -13
- data/lib/ransack/translate.rb +8 -8
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +4 -16
- data/ransack.gemspec +1 -0
- data/spec/mongoid/adapters/mongoid/base_spec.rb +276 -0
- data/spec/mongoid/adapters/mongoid/context_spec.rb +56 -0
- data/spec/mongoid/configuration_spec.rb +66 -0
- data/spec/mongoid/dependencies_spec.rb +8 -0
- data/spec/mongoid/helpers/ransack_helper.rb +11 -0
- data/spec/mongoid/nodes/condition_spec.rb +34 -0
- data/spec/mongoid/nodes/grouping_spec.rb +13 -0
- data/spec/mongoid/predicate_spec.rb +155 -0
- data/spec/mongoid/search_spec.rb +446 -0
- data/spec/mongoid/support/mongoid.yml +6 -0
- data/spec/mongoid/support/schema.rb +128 -0
- data/spec/mongoid/translate_spec.rb +14 -0
- data/spec/mongoid_spec_helper.rb +59 -0
- data/spec/ransack/adapters/active_record/base_spec.rb +68 -35
- data/spec/ransack/dependencies_spec.rb +3 -1
- data/spec/ransack/helpers/form_builder_spec.rb +6 -6
- data/spec/ransack/helpers/form_helper_spec.rb +114 -47
- data/spec/ransack/nodes/condition_spec.rb +2 -2
- data/spec/ransack/search_spec.rb +2 -6
- data/spec/ransack/translate_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -3
- data/spec/support/schema.rb +9 -0
- metadata +49 -4
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'ransack/visitor'
|
2
|
+
|
3
|
+
module Ransack
|
4
|
+
class Context
|
5
|
+
attr_reader :arel_visitor
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def for_class(klass, options = {})
|
10
|
+
if klass < ActiveRecord::Base
|
11
|
+
Adapters::ActiveRecord::Context.new(klass, options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def for_object(object, options = {})
|
16
|
+
case object
|
17
|
+
when ActiveRecord::Relation
|
18
|
+
Adapters::ActiveRecord::Context.new(object.klass, options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end # << self
|
23
|
+
|
24
|
+
def initialize(object, options = {})
|
25
|
+
@object = relation_for(object)
|
26
|
+
@klass = @object.klass
|
27
|
+
@join_dependency = join_dependency(@object)
|
28
|
+
@join_type = options[:join_type] || Polyamorous::OuterJoin
|
29
|
+
@search_key = options[:search_key] || Ransack.options[:search_key]
|
30
|
+
|
31
|
+
if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
|
32
|
+
@base = @join_dependency.join_root
|
33
|
+
@engine = @base.base_klass.arel_engine
|
34
|
+
else
|
35
|
+
@base = @join_dependency.join_base
|
36
|
+
@engine = @base.arel_engine
|
37
|
+
end
|
38
|
+
|
39
|
+
@default_table = Arel::Table.new(
|
40
|
+
@base.table_name, :as => @base.aliased_table_name, :engine => @engine
|
41
|
+
)
|
42
|
+
@bind_pairs = Hash.new do |hash, key|
|
43
|
+
parent, attr_name = get_parent_and_attribute_name(key.to_s)
|
44
|
+
if parent && attr_name
|
45
|
+
hash[key] = [parent, attr_name]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def klassify(obj)
|
51
|
+
if Class === obj && ::ActiveRecord::Base > obj
|
52
|
+
obj
|
53
|
+
elsif obj.respond_to? :klass
|
54
|
+
obj.klass
|
55
|
+
elsif obj.respond_to? :active_record # Rails 3
|
56
|
+
obj.active_record
|
57
|
+
elsif obj.respond_to? :base_klass # Rails 4
|
58
|
+
obj.base_klass
|
59
|
+
else
|
60
|
+
raise ArgumentError, "Don't know how to klassify #{obj.inspect}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Ransack
|
2
|
+
module Nodes
|
3
|
+
class Condition
|
4
|
+
|
5
|
+
def arel_predicate
|
6
|
+
predicates = attributes.map do |attr|
|
7
|
+
attr.attr.send(
|
8
|
+
arel_predicate_for_attribute(attr),
|
9
|
+
formatted_values_for_attribute(attr)
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
if predicates.size > 1
|
14
|
+
case combinator
|
15
|
+
when Constants::AND
|
16
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::And.new(predicates))
|
17
|
+
when Constants::OR
|
18
|
+
predicates.inject(&:or)
|
19
|
+
end
|
20
|
+
else
|
21
|
+
return_predicate(predicates.first)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# FIXME: Improve this edge case patch for Arel >= 6.0 (Rails >= 4.2)
|
28
|
+
# that adds several conditionals to handle changing Arel API.
|
29
|
+
# Related to Ransack issue #472 and pull requests #486-488.
|
30
|
+
#
|
31
|
+
def return_predicate(predicate)
|
32
|
+
if casted_array_with_in_predicate?(predicate)
|
33
|
+
predicate.right[0] = predicate.right[0].val
|
34
|
+
.map { |v| v.is_a?(String) ? Arel::Nodes.build_quoted(v) : v }
|
35
|
+
end
|
36
|
+
predicate
|
37
|
+
end
|
38
|
+
#
|
39
|
+
def casted_array_with_in_predicate?(predicate)
|
40
|
+
return unless defined?(Arel::Nodes::Casted)
|
41
|
+
predicate.class == Arel::Nodes::In &&
|
42
|
+
predicate.right.is_a?(Array) &&
|
43
|
+
predicate.right[0].class == Arel::Nodes::Casted
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Ransack
|
2
|
+
module Translate
|
3
|
+
|
4
|
+
def self.i18n_key(klass)
|
5
|
+
if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
6
|
+
klass.model_name.i18n_key.to_s.tr('.'.freeze, '/'.freeze)
|
7
|
+
else
|
8
|
+
klass.model_name.i18n_key.to_s.freeze
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ransack
|
2
|
+
class Visitor
|
3
|
+
def visit_and(object)
|
4
|
+
nodes = object.values.map { |o| accept(o) }.compact
|
5
|
+
return nil unless nodes.size > 0
|
6
|
+
|
7
|
+
if nodes.size > 1
|
8
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::And.new(nodes))
|
9
|
+
else
|
10
|
+
nodes.first
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def quoted?(object)
|
15
|
+
case object
|
16
|
+
when Arel::Nodes::SqlLiteral, Bignum, Fixnum
|
17
|
+
false
|
18
|
+
else
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'ransack/adapters/mongoid/base'
|
2
|
+
::Mongoid::Document.send :include, Ransack::Adapters::Mongoid::Base
|
3
|
+
|
4
|
+
require 'ransack/adapters/mongoid/attributes/attribute'
|
5
|
+
require 'ransack/adapters/mongoid/table'
|
6
|
+
require 'ransack/adapters/mongoid/inquiry_hash'
|
7
|
+
|
8
|
+
case ::Mongoid::VERSION
|
9
|
+
when /^3\.2\./
|
10
|
+
require 'ransack/adapters/mongoid/3.2/context'
|
11
|
+
else
|
12
|
+
require 'ransack/adapters/mongoid/context'
|
13
|
+
end
|
File without changes
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'ransack/adapters/mongoid/attributes/predications'
|
2
|
+
require 'ransack/adapters/mongoid/attributes/order_predications'
|
3
|
+
|
4
|
+
module Ransack
|
5
|
+
module Adapters
|
6
|
+
module Mongoid
|
7
|
+
module Attributes
|
8
|
+
class Attribute < Struct.new :relation, :name
|
9
|
+
# include Arel::Expressions
|
10
|
+
# include Arel::Predications
|
11
|
+
# include Arel::AliasPredication
|
12
|
+
# include Arel::OrderPredications
|
13
|
+
# include Arel::Math
|
14
|
+
|
15
|
+
include ::Ransack::Adapters::Mongoid::Attributes::Predications
|
16
|
+
include ::Ransack::Adapters::Mongoid::Attributes::OrderPredications
|
17
|
+
|
18
|
+
###
|
19
|
+
# Create a node for lowering this attribute
|
20
|
+
def lower
|
21
|
+
relation.lower self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class String < Attribute; end
|
26
|
+
class Time < Attribute; end
|
27
|
+
class Boolean < Attribute; end
|
28
|
+
class Decimal < Attribute; end
|
29
|
+
class Float < Attribute; end
|
30
|
+
class Integer < Attribute; end
|
31
|
+
class Undefined < Attribute; end
|
32
|
+
end
|
33
|
+
|
34
|
+
Attribute = Attributes::Attribute
|
35
|
+
end # Attributes
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Ransack
|
2
|
+
module Adapters
|
3
|
+
module Mongoid
|
4
|
+
module Attributes
|
5
|
+
module Predications
|
6
|
+
def not_eq(other)
|
7
|
+
{ name => { '$ne' => other } }.to_inquiry
|
8
|
+
end
|
9
|
+
|
10
|
+
def not_eq_any(others)
|
11
|
+
grouping_any :not_eq, others
|
12
|
+
end
|
13
|
+
|
14
|
+
def not_eq_all(others)
|
15
|
+
grouping_all :not_eq, others
|
16
|
+
end
|
17
|
+
|
18
|
+
def eq(other)
|
19
|
+
{ name => other }.to_inquiry
|
20
|
+
end
|
21
|
+
|
22
|
+
def eq_any(others)
|
23
|
+
grouping_any :eq, others
|
24
|
+
end
|
25
|
+
|
26
|
+
def eq_all(others)
|
27
|
+
grouping_all :eq, others
|
28
|
+
end
|
29
|
+
|
30
|
+
def in(other)
|
31
|
+
{ name => { "$in" => other } }.to_inquiry
|
32
|
+
end
|
33
|
+
|
34
|
+
def in_any(others)
|
35
|
+
grouping_any :in, others
|
36
|
+
end
|
37
|
+
|
38
|
+
def in_all(others)
|
39
|
+
grouping_all :in, others
|
40
|
+
end
|
41
|
+
|
42
|
+
def not_in(other)
|
43
|
+
{ "$not" => { name => { "$in" => other } } }.to_inquiry
|
44
|
+
end
|
45
|
+
|
46
|
+
def not_in_any(others)
|
47
|
+
grouping_any :not_in, others
|
48
|
+
end
|
49
|
+
|
50
|
+
def not_in_all(others)
|
51
|
+
grouping_all :not_in, others
|
52
|
+
end
|
53
|
+
|
54
|
+
def matches(other)
|
55
|
+
{ name => /#{other}/i }.to_inquiry
|
56
|
+
end
|
57
|
+
|
58
|
+
def matches_any(others)
|
59
|
+
grouping_any :matches, others
|
60
|
+
end
|
61
|
+
|
62
|
+
def matches_all(others)
|
63
|
+
grouping_all :matches, others
|
64
|
+
end
|
65
|
+
|
66
|
+
def does_not_match(other)
|
67
|
+
{ "$not" => { name => /#{other}/i } }.to_inquiry
|
68
|
+
end
|
69
|
+
|
70
|
+
def does_not_match_any(others)
|
71
|
+
grouping_any :does_not_match, others
|
72
|
+
end
|
73
|
+
|
74
|
+
def does_not_match_all(others)
|
75
|
+
grouping_all :does_not_match, others
|
76
|
+
end
|
77
|
+
|
78
|
+
def gteq(right)
|
79
|
+
{ name => { '$gte' => right } }.to_inquiry
|
80
|
+
end
|
81
|
+
|
82
|
+
def gteq_any(others)
|
83
|
+
grouping_any :gteq, others
|
84
|
+
end
|
85
|
+
|
86
|
+
def gteq_all(others)
|
87
|
+
grouping_all :gteq, others
|
88
|
+
end
|
89
|
+
|
90
|
+
def gt(right)
|
91
|
+
{ name => { '$gt' => right } }.to_inquiry
|
92
|
+
end
|
93
|
+
|
94
|
+
def gt_any(others)
|
95
|
+
grouping_any :gt, others
|
96
|
+
end
|
97
|
+
|
98
|
+
def gt_all(others)
|
99
|
+
grouping_all :gt, others
|
100
|
+
end
|
101
|
+
|
102
|
+
def lt(right)
|
103
|
+
{ name => { '$lt' => right } }.to_inquiry
|
104
|
+
end
|
105
|
+
|
106
|
+
def lt_any(others)
|
107
|
+
grouping_any :lt, others
|
108
|
+
end
|
109
|
+
|
110
|
+
def lt_all(others)
|
111
|
+
grouping_all :lt, others
|
112
|
+
end
|
113
|
+
|
114
|
+
def lteq(right)
|
115
|
+
{ name => { '$lte' => right } }.to_inquiry
|
116
|
+
end
|
117
|
+
|
118
|
+
def lteq_any(others)
|
119
|
+
grouping_any :lteq, others
|
120
|
+
end
|
121
|
+
|
122
|
+
def lteq_all(others)
|
123
|
+
grouping_all :lteq, others
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def grouping_any(method_id, others)
|
129
|
+
nodes = others.map { |e| send(method_id, e) }
|
130
|
+
{ "$or" => nodes }.to_inquiry
|
131
|
+
end
|
132
|
+
|
133
|
+
def grouping_all(method_id, others)
|
134
|
+
nodes = others.map { |e| send(method_id, e) }
|
135
|
+
{ "$and" => nodes }.to_inquiry
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Ransack
|
4
|
+
module Adapters
|
5
|
+
module Mongoid
|
6
|
+
module Base
|
7
|
+
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
end
|
12
|
+
|
13
|
+
class ColumnWrapper < SimpleDelegator
|
14
|
+
def type
|
15
|
+
_super = super
|
16
|
+
case _super
|
17
|
+
when BSON::ObjectId, Object
|
18
|
+
:string
|
19
|
+
else
|
20
|
+
_super.name.underscore.to_sym
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Connection
|
26
|
+
def initialize model
|
27
|
+
@model = model
|
28
|
+
end
|
29
|
+
|
30
|
+
def quote_column_name name
|
31
|
+
name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
def _ransackers
|
37
|
+
@_ransackers ||= {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def _ransackers=(value)
|
41
|
+
@_ransackers = value
|
42
|
+
end
|
43
|
+
|
44
|
+
def ransack(params = {}, options = {})
|
45
|
+
params = params.presence || {}
|
46
|
+
Search.new(self, params ? params.delete_if {
|
47
|
+
|k, v| v.blank? && v != false } : params, options)
|
48
|
+
end
|
49
|
+
|
50
|
+
alias_method :search, :ransack
|
51
|
+
|
52
|
+
def ransacker(name, opts = {}, &block)
|
53
|
+
self._ransackers = _ransackers.merge name.to_s => Ransacker
|
54
|
+
.new(self, name, opts, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def all_ransackable_attributes
|
58
|
+
['id'] + column_names.select { |c| c != '_id' } + _ransackers.keys
|
59
|
+
end
|
60
|
+
|
61
|
+
def ransackable_attributes(auth_object = nil)
|
62
|
+
all_ransackable_attributes
|
63
|
+
end
|
64
|
+
|
65
|
+
def ransortable_attributes(auth_object = nil)
|
66
|
+
# Here so users can overwrite the attributes
|
67
|
+
# that show up in the sort_select
|
68
|
+
ransackable_attributes(auth_object)
|
69
|
+
end
|
70
|
+
|
71
|
+
def ransackable_associations(auth_object = nil)
|
72
|
+
reflect_on_all_associations_all.map { |a| a.name.to_s }
|
73
|
+
end
|
74
|
+
|
75
|
+
def reflect_on_all_associations_all
|
76
|
+
reflect_on_all_associations(:belongs_to, :has_one, :has_many)
|
77
|
+
end
|
78
|
+
|
79
|
+
# For overriding with a whitelist of symbols
|
80
|
+
def ransackable_scopes(auth_object = nil)
|
81
|
+
[]
|
82
|
+
end
|
83
|
+
|
84
|
+
# imitating active record
|
85
|
+
|
86
|
+
def joins_values *args
|
87
|
+
[]
|
88
|
+
end
|
89
|
+
|
90
|
+
def first(*args)
|
91
|
+
if args.size == 0
|
92
|
+
super
|
93
|
+
else
|
94
|
+
self.criteria.limit(args.first)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# def group_by *args, &block
|
99
|
+
# criteria
|
100
|
+
# end
|
101
|
+
|
102
|
+
def columns
|
103
|
+
@columns ||= fields.map(&:second).map{ |c| ColumnWrapper.new(c) }
|
104
|
+
end
|
105
|
+
|
106
|
+
def column_names
|
107
|
+
@column_names ||= fields.map(&:first)
|
108
|
+
end
|
109
|
+
|
110
|
+
def columns_hash
|
111
|
+
columns.index_by(&:name)
|
112
|
+
end
|
113
|
+
|
114
|
+
def table
|
115
|
+
name = ::Ransack::Adapters::Mongoid::Attributes::Attribute.new(self.criteria, :name)
|
116
|
+
{
|
117
|
+
:name => name
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end # Base
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|