arlj 0.0.1 → 0.0.2

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
2
  SHA1:
3
- metadata.gz: 674e9b2df96b4850dc128bc5de58252833a7ba95
4
- data.tar.gz: aa92466cd390f09ad567acbad0a05a13cdfa995f
3
+ metadata.gz: a58db94fac67a5fa42539b8b1cc30beaf1134207
4
+ data.tar.gz: 3ffd40c1670e27ed4e6eefe82b20df628dd7b361
5
5
  SHA512:
6
- metadata.gz: f6498872bd416cb091ab5a69ded518af390097483583a72bb00217598373da17f608f2234ce49376978673acbd7b5e479120f1567f261ba0270ad43e449b1253
7
- data.tar.gz: 6ed666eb60114b8225dd5ad04e51cc07094cbd81e3be9f8170af3c38e30606c2c7424f2c58aa651dcb17ac6bc4488b52547a9dbbd3f6737260c72c7ffd1ff368
6
+ metadata.gz: caf5c2c6a30881344c5f75074a5a1131263f68614574b28f3790a475008d638cd8745dbdbc3c0f0d7085d4bc863d112b310020b14ba06d3fa3613d133ef803d8
7
+ data.tar.gz: 5276383d71fdf062b199cd3370dfac5258fccf7856f5a753e8444dd5a741ab8d64ec19ba2c34b79d005e14d0286502ee4f89108bb5e5d49a159267ab7e2237ff
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.5
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 fengb
1
+ Copyright (c) 2014 Benjamin Feng
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Arlj - ActiveRecord Left Join
1
+ # Arlj - ActiveRecord Left Join [![Travis CI](https://travis-ci.org/fengb/arlj.svg?branch=master)](https://travis-ci.org/fengb/arlj)
2
2
 
3
3
  Make left joins feel like first-class citizens in ActiveRecord.
4
4
 
@@ -37,7 +37,7 @@ ActiveRecord::Base.extend Arlj
37
37
  Then begin to left join!
38
38
 
39
39
  ```ruby
40
- puts Parent.arlj(:children).group('records.id').select('COUNT(children.id)').to_sql
40
+ puts Parent.left_joins(:children).group('records.id').select('COUNT(children.id)').to_sql
41
41
  => SELECT COUNT(children.id)
42
42
  FROM "parents"
43
43
  LEFT OUTER JOIN "children"
@@ -45,12 +45,12 @@ puts Parent.arlj(:children).group('records.id').select('COUNT(children.id)').to_
45
45
  GROUP BY records.id
46
46
  ```
47
47
 
48
- `arlj` is purposely low level to be extra chainable.
48
+ `left_joins` is purposely low level to be extra chainable.
49
49
 
50
- Arlj also adds an aggregation syntax:
50
+ Arlj also adds an aggregation method:
51
51
 
52
52
  ```ruby
53
- Parent.arlj_aggregate(:children, 'COUNT(*)', 'SUM(col)' => 'total').select('children_count', 'total').to_sql
53
+ Parent.left_joins_aggregate(:children, 'COUNT(*)', 'SUM(col)' => 'total').select('children_count', 'total').to_sql
54
54
  => SELECT children_count
55
55
  , total
56
56
  FROM "parents"
@@ -62,22 +62,29 @@ Parent.arlj_aggregate(:children, 'COUNT(*)', 'SUM(col)' => 'total').select('chil
62
62
  ON arlj_aggregate_children."parent_id" = "parents"."id"
63
63
  ```
64
64
 
65
- `arlj_aggregate` currently uses a subquery to hide its aggregation. It's not the
66
- most efficient implementation but it does offer a much better chaining
65
+ `left_joins_aggregate` currently uses a subquery to hide its aggregation. It is
66
+ not the most efficient implementation but it does offer a much better chaining
67
67
  experience than using `group` at the top level.
68
68
 
69
- Arlj can also alias `arlj` and `arlj_aggregate` to `left_joins` and
70
- `left_joins_aggregate` respectively:
69
+ If you prefer, you may also use `arlj` and `arlj_aggregate` instead of
70
+ `left_joins` and `left_joins_aggregate` respectively. To prevent potential
71
+ naming conflicts, please use `Arlj::Base`:
71
72
 
72
73
  ```ruby
73
74
  class Parent < ActiveRecord::Base
74
- extend Arlj::LeftJoins
75
+ extend Arlj::Base
75
76
  end
77
+ ```
78
+
79
+ **Arlj** has an experimental flag that uses the **memoist** gem to memoize the
80
+ generated join SQL:
76
81
 
77
- Parent.left_joins(:children).group('records.id').select('COUNT(children.id)')
78
- Parent.left_joins_aggregate(:children, 'COUNT(*)', 'SUM(col)' => 'total')
82
+ ```ruby
83
+ Arlj.memoize!
79
84
  ```
80
85
 
86
+ This has not been proven to be faster.
87
+
81
88
  ## TODO
82
89
 
83
90
  * Relations with conditions
data/Rakefile CHANGED
@@ -1,2 +1,6 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
2
3
 
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/arlj.gemspec CHANGED
@@ -19,10 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_runtime_dependency 'activerecord', '>= 3.1'
22
- spec.add_runtime_dependency 'memoist', '~> 0.11.0'
23
22
 
24
23
  spec.add_development_dependency 'bundler', '~> 1.7'
25
24
  spec.add_development_dependency 'rake', '~> 10.0'
26
25
  spec.add_development_dependency 'rspec', '~> 3.0'
27
26
  spec.add_development_dependency 'temping', '~> 3.2'
27
+ spec.add_development_dependency 'wrong', '~> 0.7'
28
28
  end
data/lib/arlj/base.rb ADDED
@@ -0,0 +1,106 @@
1
+ module Arlj
2
+ module Base
3
+ def self.memoize!
4
+ require 'memoist'
5
+ self.extend Memoist
6
+ self.memoize :arlj_sql, :arlj_aggregate_sql
7
+ end
8
+
9
+ def arlj(assoc)
10
+ # Example snippet:
11
+ # LEFT JOIN [assoc]
12
+ # ON [assoc].source_id = source.id
13
+
14
+ joins(arlj_sql(assoc))
15
+ end
16
+
17
+ # Example usage:
18
+ # arlj_aggregate(other, 'count(*)', 'sum(col)' => target_name)
19
+ def arlj_aggregate(assoc, *args)
20
+ # Example snippet:
21
+ # LEFT JOIN(SELECT source_id, [func]([column]) AS [target_name]
22
+ # FROM [assoc]
23
+ # GROUP BY [assoc].source_id) arlj_aggregate_[assoc]
24
+ # ON [assoc].source_id = source.id
25
+
26
+ joins(arlj_aggregate_sql(assoc, *args))
27
+ end
28
+
29
+ def arlj_arel(assoc)
30
+ refl = reflect_on_association(assoc)
31
+ arlj_left_join_arel(refl.klass.arel_table, refl.foreign_key)
32
+ end
33
+
34
+ def arlj_sql(assoc)
35
+ arlj_arel(assoc).join_sources
36
+ end
37
+
38
+ def arlj_aggregate_arel(assoc, *args)
39
+ options = args.extract_options!
40
+
41
+ refl = reflect_on_association(assoc)
42
+ refl_arel = refl.klass.arel_table
43
+
44
+ join_name = "arlj_aggregate_#{refl.table_name}"
45
+
46
+ columns = [refl_arel[refl.foreign_key]]
47
+ args.each do |thunk|
48
+ columns << parse_thunk(refl, assoc, refl_arel, thunk)
49
+ end
50
+ options.each do |thunk, name|
51
+ columns << parse_thunk(refl, assoc, refl_arel, thunk, name)
52
+ end
53
+
54
+ subq_arel =
55
+ refl_arel.project(columns).
56
+ from(refl_arel).
57
+ group(refl_arel[refl.foreign_key]).
58
+ as(join_name)
59
+
60
+ arlj_left_join_arel(subq_arel, refl.foreign_key)
61
+ end
62
+
63
+ def arlj_aggregate_sql(assoc, *args)
64
+ arlj_aggregate_arel(assoc, *args).join_sources
65
+ end
66
+
67
+ private
68
+
69
+ THUNK_PATTERN = /^([a-zA-Z]*)\((.*)\)$/
70
+ AGGREGATE_FUNCTIONS = {
71
+ 'sum' => 'sum',
72
+ 'average' => 'average',
73
+ 'avg' => 'average',
74
+ 'maximum' => 'maximum',
75
+ 'max' => 'maximum',
76
+ 'minimum' => 'minimum',
77
+ 'min' => 'minimum',
78
+ 'count' => 'count',
79
+ }.freeze
80
+ def parse_thunk(refl, assoc, arel, thunk, name=nil)
81
+ matchdata = THUNK_PATTERN.match(thunk)
82
+ if matchdata.nil?
83
+ raise "'#{thunk}' not parsable - must be of format 'func(column)'"
84
+ end
85
+
86
+ func = AGGREGATE_FUNCTIONS[matchdata[1].downcase]
87
+ if func.nil?
88
+ raise "'#{matchdata[1]}' not recognized - must be one of #{AGGREGATE_FUNCTIONS.keys}"
89
+ end
90
+
91
+ if matchdata[2] == '*'
92
+ column = refl.active_record_primary_key
93
+ name ||= "#{assoc}_#{func}"
94
+ else
95
+ column = matchdata[2]
96
+ name ||= "#{assoc}_#{func}_#{column}"
97
+ end
98
+ arel[column].send(func).as(name)
99
+ end
100
+
101
+ def arlj_left_join_arel(arel, foreign_key)
102
+ arel_table.join(arel, Arel::Nodes::OuterJoin).
103
+ on(arel[foreign_key].eq(arel_table[self.primary_key]))
104
+ end
105
+ end
106
+ end
data/lib/arlj/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Arlj
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/arlj.rb CHANGED
@@ -1,98 +1,15 @@
1
- require 'memoist'
1
+ require 'arlj/base'
2
2
 
3
3
  module Arlj
4
- autoload :LeftJoins, 'arlj/left_joins'
5
- autoload :VERSION, 'arlj/version'
4
+ autoload :Version, 'arlj/version'
5
+ include Arlj::Base
6
6
 
7
- extend Memoist
7
+ alias_method :left_joins, :arlj
8
+ alias_method :left_joins_aggregate, :arlj_aggregate
9
+ alias_method :left_joins_arel, :arlj_arel
10
+ alias_method :left_joins_aggregate_arel, :arlj_aggregate_arel
8
11
 
9
- def arlj(assoc)
10
- # Example snippet:
11
- # LEFT JOIN [assoc]
12
- # ON [assoc].source_id = source.id
13
-
14
- refl = reflect_on_association(assoc)
15
- sources = arlj_arel_sources(refl.klass.arel_table, refl.foreign_key)
16
- joins(sources)
17
- end
18
-
19
- # Example usage:
20
- # arlj_aggregate(other, 'count(*)', 'sum(col)' => target_name)
21
- def arlj_aggregate(assoc, *args)
22
- # Example snippet:
23
- # LEFT JOIN(SELECT source_id, [func]([column]) AS [target_name]
24
- # FROM [assoc]
25
- # GROUP BY [assoc].source_id) arlj_aggregate_[assoc]
26
- # ON [assoc].source_id = source.id
27
-
28
- sources = arlj_aggregate_sources(assoc, *args)
29
- joins(sources)
30
- end
31
-
32
- private
33
-
34
- THUNK_PATTERN = /^([a-zA-Z]*)\((.*)\)$/
35
- AGGREGATE_FUNCTIONS = {
36
- 'sum' => 'sum',
37
- 'average' => 'average',
38
- 'avg' => 'average',
39
- 'maximum' => 'maximum',
40
- 'max' => 'maximum',
41
- 'minimum' => 'minimum',
42
- 'min' => 'minimum',
43
- 'count' => 'count',
44
- }.freeze
45
- def parse_thunk(refl, assoc, arel, thunk, name=nil)
46
- matchdata = THUNK_PATTERN.match(thunk)
47
- if matchdata.nil?
48
- raise "'#{thunk}' not parsable - must be of format 'func(column)'"
49
- end
50
-
51
- func = AGGREGATE_FUNCTIONS[matchdata[1].downcase]
52
- if func.nil?
53
- raise "'#{matchdata[1]}' not recognized - must be one of #{AGGREGATE_FUNCTIONS.keys}"
54
- end
55
-
56
- if matchdata[2] == '*'
57
- column = refl.active_record_primary_key
58
- name ||= "#{assoc}_#{func}"
59
- else
60
- column = matchdata[2]
61
- name ||= "#{assoc}_#{func}_#{column}"
62
- end
63
- arel[column].send(func).as(name)
64
- end
65
-
66
- def arlj_aggregate_sources(assoc, *args)
67
- options = args.extract_options!
68
-
69
- refl = reflect_on_association(assoc)
70
- refl_arel = refl.klass.arel_table
71
-
72
- join_name = "arlj_aggregate_#{refl.table_name}"
73
-
74
- columns = [refl_arel[refl.foreign_key]]
75
- args.each do |thunk|
76
- columns << parse_thunk(refl, assoc, refl_arel, thunk)
77
- end
78
- options.each do |thunk, name|
79
- columns << parse_thunk(refl, assoc, refl_arel, thunk, name)
80
- end
81
-
82
- subq_arel =
83
- refl_arel.project(columns).
84
- from(refl_arel).
85
- group(refl_arel[refl.foreign_key]).
86
- as(join_name)
87
-
88
- arlj_arel_sources(subq_arel, refl.foreign_key)
89
- end
90
- memoize :arlj_aggregate_sources
91
-
92
- def arlj_arel_sources(arel, foreign_key)
93
- arel_join =
94
- arel_table.join(arel, Arel::Nodes::OuterJoin).
95
- on(arel[foreign_key].eq(arel_table[self.primary_key]))
96
- arel_join.join_sources
12
+ def self.memoize!
13
+ Arlj::Base.memoize!
97
14
  end
98
15
  end
@@ -3,13 +3,13 @@ require 'spec_helper'
3
3
  require 'arlj'
4
4
  require 'temping'
5
5
 
6
- RSpec.describe Arlj do
6
+ RSpec.describe Arlj::Base do
7
7
  Temping.create :parent do
8
8
  with_columns do |t|
9
9
  t.string :name
10
10
  end
11
11
 
12
- extend Arlj
12
+ extend Arlj::Base
13
13
 
14
14
  has_many :children
15
15
  end
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
- require 'arlj/left_joins'
3
+ require 'arlj'
4
4
 
5
- RSpec.describe Arlj::LeftJoins do
5
+ RSpec.describe Arlj, 'left_joins' do
6
6
  class LjParent < Parent
7
- extend Arlj::LeftJoins
7
+ extend Arlj
8
8
  end
9
9
 
10
10
  specify '#left_joins does arlj stuff' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arlj
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Feng
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-04 00:00:00.000000000 Z
11
+ date: 2014-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.1'
27
- - !ruby/object:Gem::Dependency
28
- name: memoist
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 0.11.0
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 0.11.0
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: bundler
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +80,20 @@ dependencies:
94
80
  - - "~>"
95
81
  - !ruby/object:Gem::Version
96
82
  version: '3.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: wrong
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.7'
97
97
  description: Make left joins feel like first-class citizens in ActiveRecord.
98
98
  email:
99
99
  - contact@fengb.info
@@ -102,15 +102,16 @@ extensions: []
102
102
  extra_rdoc_files: []
103
103
  files:
104
104
  - ".gitignore"
105
+ - ".travis.yml"
105
106
  - Gemfile
106
107
  - LICENSE.txt
107
108
  - README.md
108
109
  - Rakefile
109
110
  - arlj.gemspec
110
111
  - lib/arlj.rb
111
- - lib/arlj/left_joins.rb
112
+ - lib/arlj/base.rb
112
113
  - lib/arlj/version.rb
113
- - spec/arlj_spec.rb
114
+ - spec/base_spec.rb
114
115
  - spec/left_joins_spec.rb
115
116
  - spec/spec_helper.rb
116
117
  homepage: https://github.com/fengb/arlj
@@ -138,6 +139,6 @@ signing_key:
138
139
  specification_version: 4
139
140
  summary: ActiveRecord Left Join
140
141
  test_files:
141
- - spec/arlj_spec.rb
142
+ - spec/base_spec.rb
142
143
  - spec/left_joins_spec.rb
143
144
  - spec/spec_helper.rb
@@ -1,10 +0,0 @@
1
- require 'arlj'
2
-
3
- module Arlj
4
- module LeftJoins
5
- include Arlj
6
-
7
- alias_method :left_joins, :arlj
8
- alias_method :left_joins_aggregate, :arlj_aggregate
9
- end
10
- end