arlj 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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