ransack 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/ransack.rb +1 -1
- data/lib/ransack/adapters/active_record.rb +18 -2
- data/lib/ransack/adapters/active_record/3.0/base.rb +34 -0
- data/lib/ransack/adapters/active_record/3.0/compat.rb +23 -0
- data/lib/ransack/adapters/active_record/3.0/context.rb +168 -0
- data/lib/ransack/adapters/active_record/3.0/join_association.rb +44 -0
- data/lib/ransack/adapters/active_record/3.0/join_dependency.rb +63 -0
- data/lib/ransack/adapters/active_record/base.rb +19 -2
- data/lib/ransack/adapters/active_record/context.rb +45 -33
- data/lib/ransack/adapters/active_record/join_association.rb +44 -0
- data/lib/ransack/adapters/active_record/join_dependency.rb +63 -0
- data/lib/ransack/context.rb +25 -65
- data/lib/ransack/helpers/form_builder.rb +10 -4
- data/lib/ransack/helpers/form_helper.rb +1 -0
- data/lib/ransack/locale/en.yml +1 -0
- data/lib/ransack/nodes.rb +1 -0
- data/lib/ransack/nodes/attribute.rb +21 -4
- data/lib/ransack/nodes/bindable.rb +29 -0
- data/lib/ransack/nodes/condition.rb +30 -28
- data/lib/ransack/nodes/sort.rb +5 -3
- data/lib/ransack/nodes/value.rb +84 -100
- data/lib/ransack/predicate.rb +1 -11
- data/lib/ransack/ransacker.rb +26 -0
- data/lib/ransack/search.rb +3 -2
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +64 -0
- data/ransack.gemspec +3 -3
- data/spec/console.rb +1 -2
- data/spec/ransack/adapters/active_record/base_spec.rb +18 -0
- data/spec/ransack/adapters/active_record/context_spec.rb +2 -2
- data/spec/ransack/helpers/form_builder_spec.rb +4 -0
- data/spec/ransack/search_spec.rb +25 -2
- data/spec/spec_helper.rb +2 -3
- data/spec/support/schema.rb +8 -0
- metadata +16 -8
data/lib/ransack/nodes/sort.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
module Ransack
|
2
2
|
module Nodes
|
3
3
|
class Sort < Node
|
4
|
-
|
4
|
+
include Bindable
|
5
|
+
|
6
|
+
attr_reader :name, :dir
|
5
7
|
i18n_word :asc, :desc
|
6
8
|
|
7
9
|
class << self
|
@@ -22,12 +24,12 @@ module Ransack
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def valid?
|
25
|
-
|
27
|
+
bound? && attr
|
26
28
|
end
|
27
29
|
|
28
30
|
def name=(name)
|
29
31
|
@name = name
|
30
|
-
|
32
|
+
context.bind(self, name) unless name.blank?
|
31
33
|
end
|
32
34
|
|
33
35
|
def dir=(dir)
|
data/lib/ransack/nodes/value.rb
CHANGED
@@ -1,22 +1,12 @@
|
|
1
1
|
module Ransack
|
2
2
|
module Nodes
|
3
3
|
class Value < Node
|
4
|
-
|
5
|
-
delegate :blank?, :to => :
|
4
|
+
attr_accessor :value
|
5
|
+
delegate :blank?, :present?, :to => :value
|
6
6
|
|
7
|
-
def initialize(context, value = nil
|
7
|
+
def initialize(context, value = nil)
|
8
8
|
super(context)
|
9
|
-
@
|
10
|
-
self.type = type if type
|
11
|
-
end
|
12
|
-
|
13
|
-
def value=(val)
|
14
|
-
@value_before_cast = value
|
15
|
-
@value = nil
|
16
|
-
end
|
17
|
-
|
18
|
-
def value
|
19
|
-
@value ||= cast_to_type(@value_before_cast, @type)
|
9
|
+
@value = value
|
20
10
|
end
|
21
11
|
|
22
12
|
def persisted?
|
@@ -25,96 +15,90 @@ module Ransack
|
|
25
15
|
|
26
16
|
def eql?(other)
|
27
17
|
self.class == other.class &&
|
28
|
-
self.
|
18
|
+
self.value == other.value
|
29
19
|
end
|
30
20
|
alias :== :eql?
|
31
21
|
|
32
22
|
def hash
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def array_of_arrays?(val)
|
116
|
-
Array === val && Array === val.first
|
117
|
-
end
|
23
|
+
value.hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def cast_to_type(type)
|
27
|
+
case type
|
28
|
+
when :date
|
29
|
+
cast_to_date(value)
|
30
|
+
when :datetime, :timestamp, :time
|
31
|
+
cast_to_time(value)
|
32
|
+
when :boolean
|
33
|
+
cast_to_boolean(value)
|
34
|
+
when :integer
|
35
|
+
cast_to_integer(value)
|
36
|
+
when :float
|
37
|
+
cast_to_float(value)
|
38
|
+
when :decimal
|
39
|
+
cast_to_decimal(value)
|
40
|
+
else
|
41
|
+
cast_to_string(value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def cast_to_date(val)
|
46
|
+
if val.respond_to?(:to_date)
|
47
|
+
val.to_date rescue nil
|
48
|
+
else
|
49
|
+
y, m, d = *[val].flatten
|
50
|
+
m ||= 1
|
51
|
+
d ||= 1
|
52
|
+
Date.new(y,m,d) rescue nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def cast_to_time(val)
|
57
|
+
if val.is_a?(Array)
|
58
|
+
Time.zone.local(*val) rescue nil
|
59
|
+
else
|
60
|
+
unless val.acts_like?(:time)
|
61
|
+
val = val.is_a?(String) ? Time.zone.parse(val) : val.to_time rescue val
|
62
|
+
end
|
63
|
+
val.in_time_zone rescue nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def cast_to_boolean(val)
|
68
|
+
if val.is_a?(String) && val.blank?
|
69
|
+
nil
|
70
|
+
else
|
71
|
+
Constants::TRUE_VALUES.include?(val)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def cast_to_string(val)
|
76
|
+
val.respond_to?(:to_s) ? val.to_s : String.new(val)
|
77
|
+
end
|
78
|
+
|
79
|
+
def cast_to_integer(val)
|
80
|
+
val.blank? ? nil : val.to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
def cast_to_float(val)
|
84
|
+
val.blank? ? nil : val.to_f
|
85
|
+
end
|
86
|
+
|
87
|
+
def cast_to_decimal(val)
|
88
|
+
if val.blank?
|
89
|
+
nil
|
90
|
+
elsif val.class == BigDecimal
|
91
|
+
val
|
92
|
+
elsif val.respond_to?(:to_d)
|
93
|
+
val.to_d
|
94
|
+
else
|
95
|
+
val.to_s.to_d
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def array_of_arrays?(val)
|
100
|
+
Array === val && Array === val.first
|
101
|
+
end
|
118
102
|
end
|
119
103
|
end
|
120
104
|
end
|
data/lib/ransack/predicate.rb
CHANGED
@@ -25,16 +25,6 @@ module Ransack
|
|
25
25
|
@compound = opts[:compound]
|
26
26
|
end
|
27
27
|
|
28
|
-
def format(vals)
|
29
|
-
if formatter
|
30
|
-
vals.select {|v| validator ? validator.call(v.value_before_cast) : !v.blank?}.
|
31
|
-
map {|v| formatter.call(v.value)}
|
32
|
-
else
|
33
|
-
vals.select {|v| validator ? validator.call(v.value_before_cast) : !v.blank?}.
|
34
|
-
map {|v| v.value}
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
28
|
def eql?(other)
|
39
29
|
self.class == other.class &&
|
40
30
|
self.name == other.name
|
@@ -47,7 +37,7 @@ module Ransack
|
|
47
37
|
|
48
38
|
def validate(vals)
|
49
39
|
if validator
|
50
|
-
vals.select {|v| validator.call(v.
|
40
|
+
vals.select {|v| validator.call(v.value)}.any?
|
51
41
|
else
|
52
42
|
vals.select {|v| !v.blank?}.any?
|
53
43
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ransack
|
2
|
+
class Ransacker
|
3
|
+
|
4
|
+
attr_reader :name, :type, :formatter, :args
|
5
|
+
|
6
|
+
delegate :call, :to => :@callable
|
7
|
+
|
8
|
+
def initialize(klass, name, opts = {}, &block)
|
9
|
+
@klass, @name = klass, name
|
10
|
+
|
11
|
+
@type = opts[:type] || :string
|
12
|
+
@args = opts[:args] || [:parent]
|
13
|
+
@formatter = opts[:formatter]
|
14
|
+
@callable = opts[:callable] || block ||
|
15
|
+
(@klass.method(name) if @klass.respond_to?(name)) ||
|
16
|
+
proc {|parent| parent.table[name]}
|
17
|
+
|
18
|
+
@klass._ransackers[name.to_s] = self
|
19
|
+
end
|
20
|
+
|
21
|
+
def attr_from(bindable)
|
22
|
+
call(*args.map {|arg| bindable.send(arg)})
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/ransack/search.rb
CHANGED
@@ -13,15 +13,16 @@ module Ransack
|
|
13
13
|
:build_and, :build_or, :build_condition,
|
14
14
|
:translate, :to => :base
|
15
15
|
|
16
|
-
def initialize(object, params = {})
|
16
|
+
def initialize(object, params = {}, options = {})
|
17
17
|
params ||= {}
|
18
18
|
@context = Context.for(object)
|
19
|
+
@context.auth_object = options[:auth_object]
|
19
20
|
@base = Nodes::And.new(@context)
|
20
21
|
build(params.with_indifferent_access)
|
21
22
|
end
|
22
23
|
|
23
24
|
def result(opts = {})
|
24
|
-
@
|
25
|
+
@context.evaluate(self, opts)
|
25
26
|
end
|
26
27
|
|
27
28
|
def build(params)
|
data/lib/ransack/version.rb
CHANGED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Ransack
|
2
|
+
class Visitor
|
3
|
+
|
4
|
+
def accept(object)
|
5
|
+
visit(object)
|
6
|
+
end
|
7
|
+
|
8
|
+
def can_accept?(object)
|
9
|
+
respond_to? DISPATCH[object.class]
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_Array(object)
|
13
|
+
object.map {|o| accept(o)}.compact
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_Ransack_Nodes_Condition(object)
|
17
|
+
object.arel_predicate if object.valid?
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_Ransack_Nodes_And(object)
|
21
|
+
nodes = object.values.map {|o| accept(o)}.compact
|
22
|
+
return nil unless nodes.size > 0
|
23
|
+
|
24
|
+
if nodes.size > 1
|
25
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::And.new(nodes))
|
26
|
+
else
|
27
|
+
nodes.first
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def visit_Ransack_Nodes_Sort(object)
|
32
|
+
object.attr.send(object.dir) if object.valid?
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_Ransack_Nodes_Or(object)
|
36
|
+
nodes = object.values.map {|o| accept(o)}.compact
|
37
|
+
return nil unless nodes.size > 0
|
38
|
+
|
39
|
+
if nodes.size > 1
|
40
|
+
nodes.inject(&:or)
|
41
|
+
else
|
42
|
+
nodes.first
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def quoted?(object)
|
47
|
+
case object
|
48
|
+
when Arel::Nodes::SqlLiteral, Bignum, Fixnum
|
49
|
+
false
|
50
|
+
else
|
51
|
+
true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit(object)
|
56
|
+
send(DISPATCH[object.class], object)
|
57
|
+
end
|
58
|
+
|
59
|
+
DISPATCH = Hash.new do |hash, klass|
|
60
|
+
hash[klass] = "visit_#{klass.name.gsub('::', '_')}"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
data/ransack.gemspec
CHANGED
@@ -14,9 +14,9 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.rubyforge_project = "ransack"
|
16
16
|
|
17
|
-
s.add_dependency 'activerecord', '~> 3.
|
18
|
-
s.add_dependency 'activesupport', '~> 3.
|
19
|
-
s.add_dependency 'actionpack', '~> 3.
|
17
|
+
s.add_dependency 'activerecord', '~> 3.0'
|
18
|
+
s.add_dependency 'activesupport', '~> 3.0'
|
19
|
+
s.add_dependency 'actionpack', '~> 3.0'
|
20
20
|
s.add_development_dependency 'rspec', '~> 2.5.0'
|
21
21
|
s.add_development_dependency 'machinist', '~> 1.0.6'
|
22
22
|
s.add_development_dependency 'faker', '~> 0.9.5'
|
data/spec/console.rb
CHANGED
@@ -2,6 +2,7 @@ Bundler.setup
|
|
2
2
|
require 'machinist/active_record'
|
3
3
|
require 'sham'
|
4
4
|
require 'faker'
|
5
|
+
require 'ransack'
|
5
6
|
|
6
7
|
Dir[File.expand_path('../../spec/{helpers,support,blueprints}/*.rb', __FILE__)].each do |f|
|
7
8
|
require f
|
@@ -18,5 +19,3 @@ end
|
|
18
19
|
|
19
20
|
Schema.create
|
20
21
|
|
21
|
-
require 'ransack'
|
22
|
-
|
@@ -24,6 +24,24 @@ module Ransack
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
describe '#ransacker' do
|
28
|
+
it 'creates ransack attributes' do
|
29
|
+
s = Person.search(:reversed_name_eq => 'htimS cirA')
|
30
|
+
s.result.should have(1).person
|
31
|
+
s.result.first.should eq Person.find_by_name('Aric Smith')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'allows access of attributes through associations' do
|
35
|
+
s = Person.search(:children_reversed_name_eq => 'htimS cirA')
|
36
|
+
s.result.to_sql.should match /"children_people"."name" = 'Aric Smith'/
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'allows an "attribute" to be an InfixOperation' do
|
40
|
+
s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith')
|
41
|
+
s.result.first.should eq Person.find_by_name('Aric Smith')
|
42
|
+
end if defined?(Arel::Nodes::InfixOperation)
|
43
|
+
end
|
44
|
+
|
27
45
|
end
|
28
46
|
end
|
29
47
|
end
|
@@ -11,14 +11,14 @@ module Ransack
|
|
11
11
|
it 'contextualizes strings to attributes' do
|
12
12
|
attribute = @c.contextualize 'children_children_parent_name'
|
13
13
|
attribute.should be_a Arel::Attributes::Attribute
|
14
|
-
attribute.name.should eq 'name'
|
14
|
+
attribute.name.to_s.should eq 'name'
|
15
15
|
attribute.relation.table_alias.should eq 'parents_people'
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'builds new associations if not yet built' do
|
19
19
|
attribute = @c.contextualize 'children_articles_title'
|
20
20
|
attribute.should be_a Arel::Attributes::Attribute
|
21
|
-
attribute.name.should eq 'title'
|
21
|
+
attribute.name.to_s.should eq 'title'
|
22
22
|
attribute.relation.name.should eq 'articles'
|
23
23
|
attribute.relation.table_alias.should be_nil
|
24
24
|
end
|