arel-helpers 2.4.0 → 2.12.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6356cdb2e00758c2e17276118d916f727a1be308
4
- data.tar.gz: 26faaf1834fd66939b376ef0caa88f7e85d6bc41
2
+ SHA256:
3
+ metadata.gz: d792fc6a4945a79243f3e4b9720c9f10ce21f5a044328f56f2b381045183589a
4
+ data.tar.gz: becca530d9ecfd5a25594ce0363cbde8d792d564358d2394a36d7040e6341384
5
5
  SHA512:
6
- metadata.gz: e07f4c676161cd241f861b48aef16411f262f09fc53d9773432e185804e062c41d117d196f553b2fb3fff834c42ec7fbd3e081ad42ed741a6ced87f66d7c1a73
7
- data.tar.gz: 9a72da308ac57e7df7dab0fce3c2909d6478f8c96170ede5c98fcf940f51f415c529838150dca14fd83ced259846c84601d312fb406b59a1545d120c43fa46c1
6
+ metadata.gz: 2d825413e0dc2f3caff9ddcbc93a4eadbb1d8814196bc515fd13b3a759969db970a9fe2df911da86def2125d724d5c651e5a39eba2d9cf873d573c913b6a51f5
7
+ data.tar.gz: 70bea7795e9bcd75058dad5aaee877f6fb1d00e40f9e62b0db8b908d1a1f0656654fe67170a1024e1776b2a458ea11622fad399cda001ffd127c5f35f8197824
data/Gemfile CHANGED
@@ -1,14 +1,3 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
5
- group :development, :test do
6
- gem 'pry-nav'
7
- gem 'rake'
8
- end
9
-
10
- group :test do
11
- gem 'rspec', '~> 2.11.0'
12
- gem 'rr', '~> 1.0.4'
13
- gem 'sqlite3'
14
- end
data/README.md CHANGED
@@ -155,7 +155,7 @@ class PostQueryBuilder < ArelHelpers::QueryBuilder
155
155
  def with_comments_by(usernames)
156
156
  reflect(
157
157
  query
158
- .joins(:comments => :author)
158
+ .joins(comments: :author)
159
159
  .where(author[:username].in(usernames))
160
160
  )
161
161
  end
@@ -187,6 +187,32 @@ PostQueryBuilder.new
187
187
  .since_yesterday
188
188
  ```
189
189
 
190
+ #### Conditional reflections
191
+
192
+ If you have parts of a query that should only be added under certain conditions you can return `reflect(query)` from your method. E.g:
193
+
194
+ ```ruby
195
+ def with_comments_by(usernames)
196
+ if usernames
197
+ reflect(
198
+ query.where(post[:title].matches("%#{title}%"))
199
+ )
200
+ else
201
+ reflect(query)
202
+ end
203
+ end
204
+ ```
205
+
206
+ This can become repetitive, and as an alternative you can choose to prepend `not_nil` to your method definition:
207
+
208
+ ```ruby
209
+ class PostQueryBuilder < ArelHelpers::QueryBuilder
210
+ not_nil def with_comments_by(usernames)
211
+ reflect(query.where(post[:title].matches("%#{title}%"))) if usernames
212
+ end
213
+ end
214
+ ```
215
+
190
216
  ## Requirements
191
217
 
192
218
  Requires ActiveRecord >= 3.1.0, < 6, tested against Ruby 2.2.4. Depends on SQLite for testing purposes.
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ require './lib/arel-helpers'
10
10
 
11
11
  Bundler::GemHelper.install_tasks
12
12
 
13
- task :default => :spec
13
+ task default: :spec
14
14
 
15
15
  desc 'Run specs'
16
16
  RSpec::Core::RakeTask.new do |t|
@@ -21,7 +21,7 @@ task :console do
21
21
  $:.push(File.dirname(__FILE__))
22
22
 
23
23
  require 'spec/env'
24
- require 'pry-nav'
24
+ require 'pry-byebug'
25
25
 
26
26
  ArelHelpers::Env.establish_connection
27
27
  ArelHelpers::Env.reset
data/arel-helpers.gemspec CHANGED
@@ -1,24 +1,26 @@
1
- $:.unshift File.join(File.dirname(__FILE__), 'lib')
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
2
2
  require 'arel-helpers/version'
3
3
 
4
4
  Gem::Specification.new do |s|
5
- s.name = "arel-helpers"
5
+ s.name = 'arel-helpers'
6
6
  s.version = ::ArelHelpers::VERSION
7
- s.authors = ["Cameron Dutro"]
8
- s.email = ["camertron@gmail.com"]
9
- s.homepage = "http://github.com/camertron"
10
-
11
- s.description = s.summary = "Useful tools to help construct database queries with ActiveRecord and Arel."
7
+ s.authors = ['Cameron Dutro']
8
+ s.email = ['camertron@gmail.com']
9
+ s.homepage = 'https://github.com/camertron/arel-helpers'
10
+ s.license = 'MIT'
11
+ s.description = s.summary = 'Useful tools to help construct database queries with ActiveRecord and Arel.'
12
12
 
13
13
  s.platform = Gem::Platform::RUBY
14
- s.has_rdoc = true
15
14
 
16
- if ENV["AR"]
17
- s.add_dependency 'activerecord', ENV["AR"]
18
- else
19
- s.add_dependency 'activerecord', '>= 3.1.0', '< 6'
20
- end
15
+ s.add_dependency 'activerecord', '>= 3.1.0', '< 7'
16
+
17
+ s.add_development_dependency 'appraisal'
18
+ s.add_development_dependency 'combustion', '~> 1.3'
19
+ s.add_development_dependency 'database_cleaner', '~> 1.8'
20
+ s.add_development_dependency 'rake', '~> 10.0'
21
+ s.add_development_dependency 'rspec', '~> 3'
22
+ s.add_development_dependency 'sqlite3', '~> 1.4.0'
21
23
 
22
24
  s.require_path = 'lib'
23
- s.files = Dir["{lib,spec}/**/*", "Gemfile", "History.txt", "README.md", "Rakefile", "arel-helpers.gemspec"]
25
+ s.files = Dir['{lib,spec}/**/*', 'Gemfile', 'History.txt', 'README.md', 'Rakefile', 'arel-helpers.gemspec']
24
26
  end
data/lib/arel-helpers.rb CHANGED
@@ -17,8 +17,9 @@ rescue
17
17
  end
18
18
 
19
19
  module ArelHelpers
20
- autoload :JoinAssociation, "arel-helpers/join_association"
20
+ autoload :Aliases, "arel-helpers/aliases"
21
21
  autoload :ArelTable, "arel-helpers/arel_table"
22
+ autoload :JoinAssociation, "arel-helpers/join_association"
22
23
  autoload :QueryBuilder, "arel-helpers/query_builder"
23
24
 
24
25
  def self.join_association(*args, &block)
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+
3
+ module ArelHelpers
4
+
5
+ module Aliases
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ def aliased_as(*args)
10
+ aliases = args.map { |name| arel_table.alias(name) }
11
+
12
+ if block_given?
13
+ yield *aliases
14
+ else
15
+ aliases
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ end
@@ -17,21 +17,34 @@ module ArelHelpers
17
17
  # For example, for HABTM associations, two join statements are required.
18
18
  # This method encapsulates that functionality and yields an intermediate object for chaining.
19
19
  # It also allows you to use an outer join instead of the default inner via the join_type arg.
20
- def join_association(table, association, join_type = Arel::Nodes::InnerJoin, &block)
21
- if ActiveRecord::VERSION::STRING >= '5.0.0'
22
- join_association_5_0(table, association, join_type, &block)
23
- elsif ActiveRecord::VERSION::STRING >= '4.2.0'
24
- join_association_4_2(table, association, join_type, &block)
25
- elsif ActiveRecord::VERSION::STRING >= '4.1.0'
26
- join_association_4_1(table, association, join_type, &block)
20
+ def join_association(table, association, join_type = Arel::Nodes::InnerJoin, options = {}, &block)
21
+ if version >= '6.1.0'
22
+ join_association_6_1_0(table, association, join_type, options, &block)
23
+ elsif version >= '6.0.0'
24
+ join_association_6_0_0(table, association, join_type, options, &block)
25
+ elsif version >= '5.2.1'
26
+ join_association_5_2_1(table, association, join_type, options, &block)
27
+ elsif version >= '5.2.0'
28
+ join_association_5_2(table, association, join_type, options, &block)
29
+ elsif version >= '5.0.0'
30
+ join_association_5_0(table, association, join_type, options, &block)
31
+ elsif version >= '4.2.0'
32
+ join_association_4_2(table, association, join_type, options, &block)
33
+ elsif version >= '4.1.0'
34
+ join_association_4_1(table, association, join_type, options, &block)
27
35
  else
28
- join_association_3_1(table, association, join_type, &block)
36
+ join_association_3_1(table, association, join_type, options, &block)
29
37
  end
30
38
  end
31
39
 
32
40
  private
33
41
 
34
- def join_association_3_1(table, association, join_type)
42
+ def version
43
+ ActiveRecord::VERSION::STRING
44
+ end
45
+
46
+ def join_association_3_1(table, association, join_type, options = {})
47
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
35
48
  associations = association.is_a?(Array) ? association : [association]
36
49
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
37
50
  manager = Arel::SelectManager.new(table)
@@ -42,8 +55,9 @@ module ArelHelpers
42
55
  end
43
56
 
44
57
  manager.join_sources.map do |assoc|
58
+ assoc.left.table_alias = aliases[assoc.left.name].name if aliases.key?(assoc.left.name)
59
+
45
60
  if block_given?
46
- # yield |assoc_name, join_conditions|
47
61
  right = yield assoc.left.name.to_sym, assoc.right
48
62
  assoc.class.new(assoc.left, right)
49
63
  else
@@ -52,7 +66,8 @@ module ArelHelpers
52
66
  end
53
67
  end
54
68
 
55
- def join_association_4_1(table, association, join_type)
69
+ def join_association_4_1(table, association, join_type, options = {})
70
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
56
71
  associations = association.is_a?(Array) ? association : [association]
57
72
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
58
73
 
@@ -63,6 +78,8 @@ module ArelHelpers
63
78
  constraint.right
64
79
  end
65
80
 
81
+ constraint.left.table_alias = aliases[constraint.left.name].name if aliases.key?(constraint.left.name)
82
+
66
83
  join_type.new(constraint.left, right)
67
84
  end
68
85
  end
@@ -72,7 +89,8 @@ module ArelHelpers
72
89
  # join_association isn't able to add to the list of bind variables
73
90
  # dynamically. To get around the problem, this method must return
74
91
  # a string.
75
- def join_association_4_2(table, association, join_type)
92
+ def join_association_4_2(table, association, join_type, options = {})
93
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
76
94
  associations = association.is_a?(Array) ? association : [association]
77
95
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
78
96
 
@@ -90,6 +108,8 @@ module ArelHelpers
90
108
  join.right
91
109
  end
92
110
 
111
+ join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
112
+
93
113
  join_type.new(join.left, right)
94
114
  end
95
115
  end
@@ -101,7 +121,8 @@ module ArelHelpers
101
121
  join_strings.join(' ')
102
122
  end
103
123
 
104
- def join_association_5_0(table, association, join_type)
124
+ def join_association_5_0(table, association, join_type, options = {})
125
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
105
126
  associations = association.is_a?(Array) ? association : [association]
106
127
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
107
128
 
@@ -120,6 +141,8 @@ module ArelHelpers
120
141
  join.right
121
142
  end
122
143
 
144
+ join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
145
+
123
146
  join_type.new(join.left, right)
124
147
  end
125
148
  end
@@ -131,6 +154,131 @@ module ArelHelpers
131
154
  join_strings.join(' ')
132
155
  end
133
156
 
157
+ def join_association_5_2(table, association, join_type, options = {})
158
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
159
+ associations = association.is_a?(Array) ? association : [association]
160
+
161
+ alias_tracker = ActiveRecord::Associations::AliasTracker.create(
162
+ table.connection, table.name, {}
163
+ )
164
+
165
+ join_dependency = ActiveRecord::Associations::JoinDependency.new(
166
+ table, table.arel_table, associations, alias_tracker
167
+ )
168
+
169
+ constraints = join_dependency.join_constraints([], join_type)
170
+
171
+ constraints.map do |join|
172
+ right = if block_given?
173
+ yield join.left.name.to_sym, join.right
174
+ else
175
+ join.right
176
+ end
177
+
178
+ join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
179
+
180
+ join_type.new(join.left, right)
181
+ end
182
+ end
183
+
184
+ def join_association_5_2_1(table, association, join_type, options = {})
185
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
186
+ associations = association.is_a?(Array) ? association : [association]
187
+
188
+ alias_tracker = ActiveRecord::Associations::AliasTracker.create(
189
+ table.connection, table.name, {}
190
+ )
191
+
192
+ join_dependency = ActiveRecord::Associations::JoinDependency.new(
193
+ table, table.arel_table, associations
194
+ )
195
+
196
+ constraints = join_dependency.join_constraints([], join_type, alias_tracker)
197
+
198
+ constraints.map do |join|
199
+ right = if block_given?
200
+ yield join.left.name.to_sym, join.right
201
+ else
202
+ join.right
203
+ end
204
+
205
+ join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
206
+
207
+ join_type.new(join.left, right)
208
+ end
209
+ end
210
+
211
+ def join_association_6_0_0(table, association, join_type, options = {})
212
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
213
+ associations = association.is_a?(Array) ? association : [association]
214
+
215
+ alias_tracker = ActiveRecord::Associations::AliasTracker.create(
216
+ table.connection, table.name, {}
217
+ )
218
+
219
+ join_dependency = ActiveRecord::Associations::JoinDependency.new(
220
+ table, table.arel_table, associations, join_type
221
+ )
222
+
223
+ constraints = join_dependency.join_constraints([], alias_tracker)
224
+
225
+ constraints.map do |join|
226
+ right = if block_given?
227
+ yield join.left.name.to_sym, join.right
228
+ else
229
+ join.right
230
+ end
231
+
232
+ join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
233
+
234
+ join_type.new(join.left, right)
235
+ end
236
+ end
237
+
238
+ def join_association_6_1_0(table, association, join_type, options = {})
239
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
240
+ associations = association.is_a?(Array) ? association : [association]
241
+
242
+ alias_tracker = ActiveRecord::Associations::AliasTracker.create(
243
+ table.connection, table.name, {}
244
+ )
245
+
246
+ join_dependency = ActiveRecord::Associations::JoinDependency.new(
247
+ table, table.arel_table, associations, join_type
248
+ )
249
+
250
+ constraints = join_dependency.join_constraints([], alias_tracker, [])
251
+
252
+ constraints.map do |join|
253
+ apply_aliases(join, aliases)
254
+
255
+ right = if block_given?
256
+ yield join.left.name.to_sym, join.right
257
+ else
258
+ join.right
259
+ end
260
+
261
+ join_type.new(join.left, right)
262
+ end
263
+ end
264
+
265
+ def apply_aliases(node, aliases)
266
+ case node
267
+ when Arel::Nodes::Join
268
+ node.left = aliases[node.left.name] || node.left
269
+ apply_aliases(node.right, aliases)
270
+ when Arel::Attributes::Attribute
271
+ node.relation = aliases[node.relation.name] || node.relation
272
+ when Arel::Nodes::And
273
+ node.children.each { |child| apply_aliases(child, aliases) }
274
+ when Arel::Nodes::Unary
275
+ apply_aliases(node.value, aliases)
276
+ when Arel::Nodes::Binary
277
+ apply_aliases(node.left, aliases)
278
+ apply_aliases(node.right, aliases)
279
+ end
280
+ end
281
+
134
282
  private
135
283
 
136
284
  def to_sql(node, table, binds)
@@ -16,6 +16,21 @@ module ArelHelpers
16
16
 
17
17
  def_delegators :@query, *TERMINAL_METHODS
18
18
 
19
+ def self.not_nil(name)
20
+ mod = Module.new do
21
+ define_method(name) do |*args|
22
+ if (value = super(*args))
23
+ value
24
+ else
25
+ reflect(query)
26
+ end
27
+ end
28
+ end
29
+
30
+ prepend mod
31
+ name
32
+ end
33
+
19
34
  def initialize(query)
20
35
  @query = query
21
36
  end
@@ -23,7 +38,7 @@ module ArelHelpers
23
38
  protected
24
39
 
25
40
  def reflect(query)
26
- self.class.new(query)
41
+ dup.tap { |obj| obj.instance_variable_set('@query'.freeze, query) }
27
42
  end
28
43
  end
29
44
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module ArelHelpers
4
- VERSION = '2.4.0'
4
+ VERSION = '2.12.0'
5
5
  end