ransack 0.5.8 → 0.6.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.
- data/.travis.yml +4 -1
- data/Gemfile +1 -1
- data/lib/ransack/adapters/active_record.rb +2 -0
- data/lib/ransack/adapters/active_record/3.0/context.rb +5 -0
- data/lib/ransack/adapters/active_record/3.1/context.rb +163 -0
- data/lib/ransack/adapters/active_record/context.rb +12 -138
- data/lib/ransack/context.rb +0 -1
- data/lib/ransack/version.rb +1 -1
- data/spec/spec_helper.rb +7 -1
- metadata +17 -16
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -4,6 +4,8 @@ ActiveRecord::Base.extend Ransack::Adapters::ActiveRecord::Base
|
|
4
4
|
case ActiveRecord::VERSION::STRING
|
5
5
|
when /^3\.0\./
|
6
6
|
require 'ransack/adapters/active_record/3.0/context'
|
7
|
+
when /^3\.1\./
|
8
|
+
require 'ransack/adapters/active_record/3.1/context'
|
7
9
|
else
|
8
10
|
require 'ransack/adapters/active_record/context'
|
9
11
|
end
|
@@ -10,6 +10,11 @@ module Ransack
|
|
10
10
|
# Because the AR::Associations namespace is insane
|
11
11
|
JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
|
12
12
|
JoinBase = JoinDependency::JoinBase
|
13
|
+
|
14
|
+
def initialize(object, options = {})
|
15
|
+
super
|
16
|
+
@arel_visitor = Arel::Visitors.visitor_for @engine
|
17
|
+
end
|
13
18
|
|
14
19
|
def evaluate(search, opts = {})
|
15
20
|
viz = Visitor.new
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'ransack/context'
|
2
|
+
require 'polyamorous'
|
3
|
+
|
4
|
+
module Ransack
|
5
|
+
module Adapters
|
6
|
+
module ActiveRecord
|
7
|
+
class Context < ::Ransack::Context
|
8
|
+
# Because the AR::Associations namespace is insane
|
9
|
+
JoinDependency = ::ActiveRecord::Associations::JoinDependency
|
10
|
+
JoinPart = JoinDependency::JoinPart
|
11
|
+
|
12
|
+
def initialize(object, options = {})
|
13
|
+
super
|
14
|
+
@arel_visitor = Arel::Visitors.visitor_for @engine
|
15
|
+
end
|
16
|
+
|
17
|
+
def evaluate(search, opts = {})
|
18
|
+
viz = Visitor.new
|
19
|
+
relation = @object.except(:order).where(viz.accept(search.base)).order(viz.accept(search.sorts))
|
20
|
+
opts[:distinct] ? relation.select("DISTINCT #{@klass.quoted_table_name}.*") : relation
|
21
|
+
end
|
22
|
+
|
23
|
+
def attribute_method?(str, klass = @klass)
|
24
|
+
exists = false
|
25
|
+
|
26
|
+
if ransackable_attribute?(str, klass)
|
27
|
+
exists = true
|
28
|
+
elsif (segments = str.split(/_/)).size > 1
|
29
|
+
remainder = []
|
30
|
+
found_assoc = nil
|
31
|
+
while !found_assoc && remainder.unshift(segments.pop) && segments.size > 0 do
|
32
|
+
assoc, poly_class = unpolymorphize_association(segments.join('_'))
|
33
|
+
if found_assoc = get_association(assoc, klass)
|
34
|
+
exists = attribute_method?(remainder.join('_'), poly_class || found_assoc.klass)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
exists
|
40
|
+
end
|
41
|
+
|
42
|
+
def table_for(parent)
|
43
|
+
parent.table
|
44
|
+
end
|
45
|
+
|
46
|
+
def klassify(obj)
|
47
|
+
if Class === obj && ::ActiveRecord::Base > obj
|
48
|
+
obj
|
49
|
+
elsif obj.respond_to? :klass
|
50
|
+
obj.klass
|
51
|
+
elsif obj.respond_to? :active_record
|
52
|
+
obj.active_record
|
53
|
+
else
|
54
|
+
raise ArgumentError, "Don't know how to klassify #{obj}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def type_for(attr)
|
59
|
+
return nil unless attr && attr.valid?
|
60
|
+
name = attr.arel_attribute.name.to_s
|
61
|
+
table = attr.arel_attribute.relation.table_name
|
62
|
+
|
63
|
+
unless @engine.connection_pool.table_exists?(table)
|
64
|
+
raise "No table named #{table} exists"
|
65
|
+
end
|
66
|
+
|
67
|
+
@engine.connection_pool.columns_hash[table][name].type
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def get_parent_and_attribute_name(str, parent = @base)
|
73
|
+
attr_name = nil
|
74
|
+
|
75
|
+
if ransackable_attribute?(str, klassify(parent))
|
76
|
+
attr_name = str
|
77
|
+
elsif (segments = str.split(/_/)).size > 1
|
78
|
+
remainder = []
|
79
|
+
found_assoc = nil
|
80
|
+
while remainder.unshift(segments.pop) && segments.size > 0 && !found_assoc do
|
81
|
+
assoc, klass = unpolymorphize_association(segments.join('_'))
|
82
|
+
if found_assoc = get_association(assoc, parent)
|
83
|
+
join = build_or_find_association(found_assoc.name, parent, klass)
|
84
|
+
parent, attr_name = get_parent_and_attribute_name(remainder.join('_'), join)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
[parent, attr_name]
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_association(str, parent = @base)
|
93
|
+
klass = klassify parent
|
94
|
+
ransackable_association?(str, klass) &&
|
95
|
+
klass.reflect_on_all_associations.detect {|a| a.name.to_s == str}
|
96
|
+
end
|
97
|
+
|
98
|
+
def join_dependency(relation)
|
99
|
+
if relation.respond_to?(:join_dependency) # Squeel will enable this
|
100
|
+
relation.join_dependency
|
101
|
+
else
|
102
|
+
build_join_dependency(relation)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_join_dependency(relation)
|
107
|
+
buckets = relation.joins_values.group_by do |join|
|
108
|
+
case join
|
109
|
+
when String
|
110
|
+
'string_join'
|
111
|
+
when Hash, Symbol, Array
|
112
|
+
'association_join'
|
113
|
+
when ::ActiveRecord::Associations::JoinDependency::JoinAssociation
|
114
|
+
'stashed_join'
|
115
|
+
when Arel::Nodes::Join
|
116
|
+
'join_node'
|
117
|
+
else
|
118
|
+
raise 'unknown class: %s' % join.class.name
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
association_joins = buckets['association_join'] || []
|
123
|
+
stashed_association_joins = buckets['stashed_join'] || []
|
124
|
+
join_nodes = buckets['join_node'] || []
|
125
|
+
string_joins = (buckets['string_join'] || []).map { |x|
|
126
|
+
x.strip
|
127
|
+
}.uniq
|
128
|
+
|
129
|
+
join_list = relation.send :custom_join_ast, relation.table.from(relation.table), string_joins
|
130
|
+
|
131
|
+
join_dependency = JoinDependency.new(
|
132
|
+
relation.klass,
|
133
|
+
association_joins,
|
134
|
+
join_list
|
135
|
+
)
|
136
|
+
|
137
|
+
join_nodes.each do |join|
|
138
|
+
join_dependency.table_aliases[join.left.name.downcase] = 1
|
139
|
+
end
|
140
|
+
|
141
|
+
join_dependency.graft(*stashed_association_joins)
|
142
|
+
end
|
143
|
+
|
144
|
+
def build_or_find_association(name, parent = @base, klass = nil)
|
145
|
+
found_association = @join_dependency.join_associations.detect do |assoc|
|
146
|
+
assoc.reflection.name == name &&
|
147
|
+
assoc.parent == parent &&
|
148
|
+
(!klass || assoc.reflection.klass == klass)
|
149
|
+
end
|
150
|
+
unless found_association
|
151
|
+
@join_dependency.send(:build, Polyamorous::Join.new(name, @join_type, klass), parent)
|
152
|
+
found_association = @join_dependency.join_associations.last
|
153
|
+
# Leverage the stashed association functionality in AR
|
154
|
+
@object = @object.joins(found_association)
|
155
|
+
end
|
156
|
+
|
157
|
+
found_association
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -1,157 +1,31 @@
|
|
1
1
|
require 'ransack/context'
|
2
|
+
require 'ransack/adapters/active_record/3.1/context'
|
2
3
|
require 'polyamorous'
|
3
4
|
|
4
5
|
module Ransack
|
5
6
|
module Adapters
|
6
7
|
module ActiveRecord
|
7
8
|
class Context < ::Ransack::Context
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
def attribute_method?(str, klass = @klass)
|
19
|
-
exists = false
|
20
|
-
|
21
|
-
if ransackable_attribute?(str, klass)
|
22
|
-
exists = true
|
23
|
-
elsif (segments = str.split(/_/)).size > 1
|
24
|
-
remainder = []
|
25
|
-
found_assoc = nil
|
26
|
-
while !found_assoc && remainder.unshift(segments.pop) && segments.size > 0 do
|
27
|
-
assoc, poly_class = unpolymorphize_association(segments.join('_'))
|
28
|
-
if found_assoc = get_association(assoc, klass)
|
29
|
-
exists = attribute_method?(remainder.join('_'), poly_class || found_assoc.klass)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
exists
|
35
|
-
end
|
36
|
-
|
37
|
-
def table_for(parent)
|
38
|
-
parent.table
|
39
|
-
end
|
40
|
-
|
41
|
-
def klassify(obj)
|
42
|
-
if Class === obj && ::ActiveRecord::Base > obj
|
43
|
-
obj
|
44
|
-
elsif obj.respond_to? :klass
|
45
|
-
obj.klass
|
46
|
-
elsif obj.respond_to? :active_record
|
47
|
-
obj.active_record
|
48
|
-
else
|
49
|
-
raise ArgumentError, "Don't know how to klassify #{obj}"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
9
|
+
|
10
|
+
# Redefine a few things that have changed with 3.2.
|
11
|
+
|
12
|
+
def initialize(object, options = {})
|
13
|
+
super
|
14
|
+
@arel_visitor = @engine.connection.visitor
|
15
|
+
end
|
16
|
+
|
53
17
|
def type_for(attr)
|
54
18
|
return nil unless attr && attr.valid?
|
55
19
|
name = attr.arel_attribute.name.to_s
|
56
20
|
table = attr.arel_attribute.relation.table_name
|
57
21
|
|
58
|
-
unless @engine.
|
22
|
+
unless @engine.connection.table_exists?(table)
|
59
23
|
raise "No table named #{table} exists"
|
60
24
|
end
|
61
25
|
|
62
|
-
@engine.
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def get_parent_and_attribute_name(str, parent = @base)
|
68
|
-
attr_name = nil
|
69
|
-
|
70
|
-
if ransackable_attribute?(str, klassify(parent))
|
71
|
-
attr_name = str
|
72
|
-
elsif (segments = str.split(/_/)).size > 1
|
73
|
-
remainder = []
|
74
|
-
found_assoc = nil
|
75
|
-
while remainder.unshift(segments.pop) && segments.size > 0 && !found_assoc do
|
76
|
-
assoc, klass = unpolymorphize_association(segments.join('_'))
|
77
|
-
if found_assoc = get_association(assoc, parent)
|
78
|
-
join = build_or_find_association(found_assoc.name, parent, klass)
|
79
|
-
parent, attr_name = get_parent_and_attribute_name(remainder.join('_'), join)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
[parent, attr_name]
|
26
|
+
@engine.connection.schema_cache.columns_hash[table][name].type
|
85
27
|
end
|
86
|
-
|
87
|
-
def get_association(str, parent = @base)
|
88
|
-
klass = klassify parent
|
89
|
-
ransackable_association?(str, klass) &&
|
90
|
-
klass.reflect_on_all_associations.detect {|a| a.name.to_s == str}
|
91
|
-
end
|
92
|
-
|
93
|
-
def join_dependency(relation)
|
94
|
-
if relation.respond_to?(:join_dependency) # Squeel will enable this
|
95
|
-
relation.join_dependency
|
96
|
-
else
|
97
|
-
build_join_dependency(relation)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def build_join_dependency(relation)
|
102
|
-
buckets = relation.joins_values.group_by do |join|
|
103
|
-
case join
|
104
|
-
when String
|
105
|
-
'string_join'
|
106
|
-
when Hash, Symbol, Array
|
107
|
-
'association_join'
|
108
|
-
when ::ActiveRecord::Associations::JoinDependency::JoinAssociation
|
109
|
-
'stashed_join'
|
110
|
-
when Arel::Nodes::Join
|
111
|
-
'join_node'
|
112
|
-
else
|
113
|
-
raise 'unknown class: %s' % join.class.name
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
association_joins = buckets['association_join'] || []
|
118
|
-
stashed_association_joins = buckets['stashed_join'] || []
|
119
|
-
join_nodes = buckets['join_node'] || []
|
120
|
-
string_joins = (buckets['string_join'] || []).map { |x|
|
121
|
-
x.strip
|
122
|
-
}.uniq
|
123
|
-
|
124
|
-
join_list = relation.send :custom_join_ast, relation.table.from(relation.table), string_joins
|
125
|
-
|
126
|
-
join_dependency = JoinDependency.new(
|
127
|
-
relation.klass,
|
128
|
-
association_joins,
|
129
|
-
join_list
|
130
|
-
)
|
131
|
-
|
132
|
-
join_nodes.each do |join|
|
133
|
-
join_dependency.table_aliases[join.left.name.downcase] = 1
|
134
|
-
end
|
135
|
-
|
136
|
-
join_dependency.graft(*stashed_association_joins)
|
137
|
-
end
|
138
|
-
|
139
|
-
def build_or_find_association(name, parent = @base, klass = nil)
|
140
|
-
found_association = @join_dependency.join_associations.detect do |assoc|
|
141
|
-
assoc.reflection.name == name &&
|
142
|
-
assoc.parent == parent &&
|
143
|
-
(!klass || assoc.reflection.klass == klass)
|
144
|
-
end
|
145
|
-
unless found_association
|
146
|
-
@join_dependency.send(:build, Polyamorous::Join.new(name, @join_type, klass), parent)
|
147
|
-
found_association = @join_dependency.join_associations.last
|
148
|
-
# Leverage the stashed association functionality in AR
|
149
|
-
@object = @object.joins(found_association)
|
150
|
-
end
|
151
|
-
|
152
|
-
found_association
|
153
|
-
end
|
154
|
-
|
28
|
+
|
155
29
|
end
|
156
30
|
end
|
157
31
|
end
|
data/lib/ransack/context.rb
CHANGED
@@ -34,7 +34,6 @@ module Ransack
|
|
34
34
|
@join_type = options[:join_type] || Arel::OuterJoin
|
35
35
|
@base = @join_dependency.join_base
|
36
36
|
@engine = @base.arel_engine
|
37
|
-
@arel_visitor = Arel::Visitors.visitor_for @engine
|
38
37
|
@default_table = Arel::Table.new(@base.table_name, :as => @base.aliased_table_name, :engine => @engine)
|
39
38
|
@bind_pairs = Hash.new do |hash, key|
|
40
39
|
parent, attr_name = get_parent_and_attribute_name(key.to_s)
|
data/lib/ransack/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -20,7 +20,13 @@ Sham.define do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
RSpec.configure do |config|
|
23
|
-
config.before(:suite)
|
23
|
+
config.before(:suite) do
|
24
|
+
puts '=' * 80
|
25
|
+
puts "Running specs against ActiveRecord #{ActiveRecord::VERSION::STRING} and ARel #{Arel::VERSION}..."
|
26
|
+
puts '=' * 80
|
27
|
+
Schema.create
|
28
|
+
end
|
29
|
+
|
24
30
|
config.before(:all) { Sham.reset(:before_all) }
|
25
31
|
config.before(:each) { Sham.reset(:before_each) }
|
26
32
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ransack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-01-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &70226024755600 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '3.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70226024755600
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: actionpack
|
27
|
-
requirement: &
|
27
|
+
requirement: &70226024754660 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '3.0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70226024754660
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: polyamorous
|
38
|
-
requirement: &
|
38
|
+
requirement: &70226024753760 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 0.5.0
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70226024753760
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &70226024753240 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 2.6.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70226024753240
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: machinist
|
60
|
-
requirement: &
|
60
|
+
requirement: &70226024752660 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.0.6
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70226024752660
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: faker
|
71
|
-
requirement: &
|
71
|
+
requirement: &70226024768120 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 0.9.5
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70226024768120
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: sqlite3
|
82
|
-
requirement: &
|
82
|
+
requirement: &70226024767020 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,7 +87,7 @@ dependencies:
|
|
87
87
|
version: 1.3.3
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70226024767020
|
91
91
|
description: Ransack is the successor to the MetaSearch gem. It improves and expands
|
92
92
|
upon MetaSearch's functionality, but does not have a 100%-compatible API.
|
93
93
|
email:
|
@@ -106,6 +106,7 @@ files:
|
|
106
106
|
- lib/ransack/adapters/active_record.rb
|
107
107
|
- lib/ransack/adapters/active_record/3.0/compat.rb
|
108
108
|
- lib/ransack/adapters/active_record/3.0/context.rb
|
109
|
+
- lib/ransack/adapters/active_record/3.1/context.rb
|
109
110
|
- lib/ransack/adapters/active_record/base.rb
|
110
111
|
- lib/ransack/adapters/active_record/context.rb
|
111
112
|
- lib/ransack/configuration.rb
|