rom-sql 0.2.0 → 0.3.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
2
  SHA1:
3
- metadata.gz: c088db5ced8d4a5297ceb909e77dba86cd80ed37
4
- data.tar.gz: 9d7f52c636a1418b966d7c609f57e766a1a4b224
3
+ metadata.gz: 2f9129b51494f9c6e4e78dd37a3700e103012ee7
4
+ data.tar.gz: 72e8eb116c43a411fb250ee866cdeeb9e5edb0a9
5
5
  SHA512:
6
- metadata.gz: 2603a384db038ddf194df6827b4d29585f48b5db8cd4791117149e292c55d3c0e8ddcec364e516b046fab29a28c32af22e964f4788c4f44082b808239b1127b3
7
- data.tar.gz: 67d0846c7bea76d5c9365d35874a9755aa1c0306cbf09de7ce51b4964c16784c740f2b8713bb632dca0d54ac3ba480fc931d3a477abd602791ed28be4ba81183
6
+ metadata.gz: 468a0785cb4d9d132bcdd344cf0bb72b12ece60b5e118d8ae7a5ac095d1be5adb7eca861ae0bf70167c79bf911c2fca30e4d814cb514c44cfc8e53b421266dcb
7
+ data.tar.gz: f081da3b8fe5f27c6b9c40f80f5b374a230ffd3e30d71b8ce70d82edeac09c176bce328bf8015d50e7f2dd51f88ea54aeb36d7661f90e5f1a6ff23807436b500
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## v0.3.0 2014-12-19
2
+
3
+ ### Changed
4
+
5
+ * `association_join` now uses Sequel's `graph` interface which qualifies columns automatically (solnic)
6
+ * Delete command returns deleted tuples (solnic)
7
+
8
+ [Compare v0.2.0...v0.3.0](https://github.com/rom-rb/rom-sql/compare/v0.2.0...v0.3.0)
9
+
1
10
  ## v0.2.0 2014-12-06
2
11
 
3
12
  ### Added
data/README.md CHANGED
@@ -58,28 +58,14 @@ end
58
58
 
59
59
  ## Relations
60
60
 
61
- ROM doesn't have a relationship concept like in ActiveRecord or Sequel. Instead
62
- it provides a convenient interface for building joined relations that can be
63
- mapped to [aggregate objects](http://martinfowler.com/bliki/Aggregate.html).
64
-
65
- There's no lazy-loading, eager-loading or any other magic happening behind the
66
- scenes. You're in full control of how data are fetched from the database and it's
67
- an explicit operation.
68
-
69
61
  ``` ruby
70
62
 
71
63
  setup.relation(:tasks)
72
64
 
73
65
  setup.relation(:users) do
74
- one_to_many :tasks, key: :user_id
75
-
76
66
  def by_name(name)
77
67
  where(name: name)
78
68
  end
79
-
80
- def with_tasks
81
- association_join(:tasks)
82
- end
83
69
  end
84
70
 
85
71
  rom = setup.finalize
@@ -94,10 +80,19 @@ puts users.by_name("Piotr").with_tasks.to_a.inspect
94
80
  # => [{:id=>1, :name=>"Piotr", :user_id=>1, :title=>"Be happy"}]
95
81
  ```
96
82
 
97
- ## Mapping
83
+ ## Mapping joins to aggregates
84
+
85
+ ROM doesn't have a relationship concept like in ActiveRecord or Sequel. Instead
86
+ it provides a convenient interface for building joined relations that can be
87
+ mapped to [aggregate objects](http://martinfowler.com/bliki/Aggregate.html).
88
+
89
+ There's no lazy-loading, eager-loading or any other magic happening behind the
90
+ scenes. You're in full control of how data are fetched from the database and it's
91
+ an explicit operation.
98
92
 
99
- Mapping joined relations can be simplified using `wrap` and `group` in-memory
100
- operations:
93
+ Sequel's association DSL is available in relation definitions which enables
94
+ `association_join` interface inside relations. To map joined results to
95
+ aggregate objects `wrap` and `group` mapping transformation can be used
101
96
 
102
97
  ``` ruby
103
98
  setup.relation(:tasks)
@@ -110,7 +105,7 @@ setup.relation(:users) do
110
105
  end
111
106
 
112
107
  def with_tasks
113
- in_memory { group(association_join(:tasks), tasks: [:title]) }
108
+ association_join(:tasks, select: [:title])
114
109
  end
115
110
  end
116
111
 
data/lib/rom/sql.rb CHANGED
@@ -9,7 +9,6 @@ end
9
9
 
10
10
  require "rom/sql/version"
11
11
  require "rom/sql/header"
12
- require "rom/sql/relation_extension"
13
12
  require "rom/sql/relation_inclusion"
14
13
  require "rom/sql/adapter"
15
14
 
@@ -41,7 +41,9 @@ module ROM
41
41
  end
42
42
 
43
43
  def extend_relation_instance(relation)
44
- relation.extend(RelationExtension)
44
+ model = relation.model
45
+ model.set_dataset(relation.dataset)
46
+ model.dataset.naked!
45
47
  end
46
48
 
47
49
  def command_namespace
@@ -38,8 +38,9 @@ module ROM
38
38
  class Delete < ROM::Commands::Delete
39
39
 
40
40
  def execute
41
+ deleted = target.to_a
41
42
  target.delete
42
- relation
43
+ deleted
43
44
  end
44
45
 
45
46
  end
@@ -25,6 +25,10 @@ module ROM
25
25
  }
26
26
  end
27
27
 
28
+ def names
29
+ map { |col| :"#{col.to_s.split('___').last}" }
30
+ end
31
+
28
32
  def project(*names)
29
33
  find_all { |col| names.include?(col) }
30
34
  end
@@ -9,6 +9,7 @@ module ROM
9
9
  klass.extend(AssociationDSL)
10
10
 
11
11
  klass.send(:undef_method, :select)
12
+ klass.send(:attr_reader, :model)
12
13
 
13
14
  klass.class_eval {
14
15
  class << self
@@ -42,8 +43,8 @@ module ROM
42
43
  # end
43
44
  #
44
45
  # @api public
45
- def association_join(*args)
46
- send(:append_association, __method__, *args)
46
+ def association_join(name, options = {})
47
+ graph_join(name, :inner, options)
47
48
  end
48
49
 
49
50
  # Join configured association
@@ -63,36 +64,53 @@ module ROM
63
64
  # end
64
65
  #
65
66
  # @api public
66
- def association_left_join(*args)
67
- send(:append_association, __method__, *args)
67
+ def association_left_join(name, options = {})
68
+ graph_join(name, :left_outer, options)
68
69
  end
69
70
 
70
- private
71
-
72
71
  # @api private
73
- def append_association(type, name, options = {})
74
- self.class.new(
75
- dataset.public_send(type, name).
76
- select_append(*columns_for_association(name, options))
77
- )
72
+ def primary_key
73
+ model.primary_key
78
74
  end
79
75
 
80
76
  # @api private
81
- def columns_for_association(name, options)
82
- col_names = options[:select]
83
-
84
- return send(Inflecto.pluralize(name)).qualified_columns unless col_names
85
-
86
- relations = col_names.is_a?(Hash) ? col_names.keys : [name]
87
-
88
- columns = relations.each_with_object([]) do |rel_name, a|
89
- relation = send(Inflecto.pluralize(rel_name))
90
- names = col_names.is_a?(Hash) ? col_names[rel_name] : col_names
91
-
92
- a.concat(relation.select(*names).prefix.qualified_columns)
77
+ def graph_join(name, join_type, options = {})
78
+ assoc = model.association_reflection(name)
79
+
80
+ key = assoc[:key]
81
+ type = assoc[:type]
82
+
83
+ if type == :many_to_many
84
+ select = options[:select] || {}
85
+
86
+ l_select, r_select =
87
+ if select.is_a?(Hash)
88
+ [select[assoc[:join_table]] || [], select[name]]
89
+ else
90
+ [[], select]
91
+ end
92
+
93
+ l_graph = graph(
94
+ assoc[:join_table],
95
+ { assoc[:left_key] => primary_key },
96
+ select: l_select, implicit_qualifier: self.name
97
+ )
98
+
99
+ l_graph.graph(name, { primary_key => assoc[:right_key] }, select: r_select)
100
+ else
101
+ join_keys =
102
+ if type == :many_to_one
103
+ { primary_key => key }
104
+ else
105
+ { key => primary_key }
106
+ end
107
+
108
+ graph(
109
+ name,
110
+ join_keys,
111
+ options.merge(join_type: join_type, implicit_qualifier: self.name)
112
+ )
93
113
  end
94
-
95
- columns
96
114
  end
97
115
 
98
116
  module AssociationDSL
@@ -1,5 +1,5 @@
1
1
  module ROM
2
2
  module SQL
3
- VERSION = "0.2.0".freeze
3
+ VERSION = "0.3.0".freeze
4
4
  end
5
5
  end
@@ -8,7 +8,7 @@ describe 'Commands / Delete' do
8
8
  before do
9
9
  setup.relation(:users) do
10
10
  def by_name(name)
11
- where(name: 'Piotr')
11
+ where(name: name)
12
12
  end
13
13
  end
14
14
 
@@ -22,12 +22,12 @@ describe 'Commands / Delete' do
22
22
  it 'deletes all tuples' do
23
23
  result = users.try { delete }
24
24
 
25
- expect(result.value.to_a).to match_array([])
25
+ expect(result.value.to_a).to match_array([{ id: 1, name: 'Piotr' }, { id: 2, name: 'Jane' }])
26
26
  end
27
27
 
28
28
  it 'deletes all tuples in a restricted relation' do
29
29
  result = users.try { delete(:by_name, 'Jane').execute }
30
30
 
31
- expect(result.value.to_a).to match_array([{ id: 2, name: 'Jane' }])
31
+ expect(result.value).to match_array([{ id: 2, name: 'Jane' }])
32
32
  end
33
33
  end
@@ -21,11 +21,11 @@ describe 'Defining multiple associations' do
21
21
  right_key: :tag_id
22
22
 
23
23
  def with_user_and_tags
24
- all.with_tags.with_user
24
+ all.with_user.with_tags
25
25
  end
26
26
 
27
27
  def all
28
- select(:id, :title).qualified
28
+ select(:id, :title)
29
29
  end
30
30
 
31
31
  def by_tag(name)
@@ -33,39 +33,41 @@ describe 'Defining multiple associations' do
33
33
  end
34
34
 
35
35
  def with_tags
36
- association_left_join(:tags, select: :name)
36
+ association_left_join(:tags, select: [:name])
37
37
  end
38
38
 
39
39
  def with_user
40
- association_join(:users, select: :name)
40
+ association_join(:users, select: [:name])
41
41
  end
42
42
 
43
- def sorted_by_tag_name
44
- order(Sequel.desc(:tasks__title))
43
+ def sorted_by_tags_name
44
+ order(Sequel.desc(:tags__name))
45
45
  end
46
46
 
47
47
  end
48
48
 
49
- expect(rom.relations.tasks.with_user_and_tags.to_a).to eql([
50
- { id: 1, title: 'Finish ROM', user_name: 'Piotr', tag_name: 'important' },
51
- { id: 2, title: 'Go to sleep', user_name: 'Piotr', tag_name: nil }
49
+ tasks = rom.relations.tasks
50
+
51
+ expect(tasks.with_user_and_tags.to_a).to eql([
52
+ { id: 1, title: 'Finish ROM', name: 'Piotr', tags_name: 'important' },
53
+ { id: 2, title: 'Go to sleep', name: 'Piotr', tags_name: nil }
52
54
  ])
53
55
 
54
- expect(rom.relations.tasks.with_user_and_tags.sorted_by_tag_name.to_a).to eql([
55
- { id: 2, title: 'Go to sleep', user_name: 'Piotr', tag_name: nil },
56
- { id: 1, title: 'Finish ROM', user_name: 'Piotr', tag_name: 'important' }
56
+ expect(tasks.with_user_and_tags.sorted_by_tags_name.to_a).to eql([
57
+ { id: 2, title: 'Go to sleep', name: 'Piotr', tags_name: nil },
58
+ { id: 1, title: 'Finish ROM', name: 'Piotr', tags_name: 'important' }
57
59
  ])
58
60
 
59
- expect(rom.relations.tasks.with_user_and_tags.by_tag('important').to_a).to eql([
60
- { id: 1, title: 'Finish ROM', user_name: 'Piotr', tag_name: 'important' }
61
+ expect(tasks.with_user_and_tags.by_tag('important').to_a).to eql([
62
+ { id: 1, title: 'Finish ROM', name: 'Piotr', tags_name: 'important' }
61
63
  ])
62
64
 
63
- expect(rom.relations.tasks.all.with_user.to_a).to eql([
64
- { id: 1, title: 'Finish ROM', user_name: 'Piotr' },
65
- { id: 2, title: 'Go to sleep', user_name: 'Piotr' }
65
+ expect(tasks.all.with_user.to_a).to eql([
66
+ { id: 1, title: 'Finish ROM', name: 'Piotr' },
67
+ { id: 2, title: 'Go to sleep', name: 'Piotr' }
66
68
  ])
67
69
 
68
- expect(rom.relations.tasks.where(title: 'Go to sleep').to_a).to eql(
70
+ expect(tasks.where(title: 'Go to sleep').to_a).to eql(
69
71
  [{ id: 2, user_id: 1, title: 'Go to sleep'}]
70
72
  )
71
73
  end
@@ -16,11 +16,21 @@ describe 'Defining many-to-one association' do
16
16
  right_key: :tag_id
17
17
 
18
18
  def with_tags
19
- association_left_join(:tags).select(:tasks__id, :tasks__title, :tags__name)
19
+ association_left_join(:tags, select: [:name])
20
+ end
21
+
22
+ def with_tags_and_tag_id
23
+ association_left_join(:tags, select: {
24
+ tags: [:name], task_tags: [:tag_id]
25
+ })
20
26
  end
21
27
 
22
28
  def by_tag(name)
23
- with_tags.where(tags__name: name)
29
+ with_tags.where(name: name)
30
+ end
31
+
32
+ def all
33
+ select(:id, :title)
24
34
  end
25
35
  end
26
36
 
@@ -28,12 +38,17 @@ describe 'Defining many-to-one association' do
28
38
 
29
39
  tasks = rom.relations.tasks
30
40
 
31
- expect(tasks.with_tags.to_a).to eql([
41
+ expect(tasks.all.with_tags.to_a).to eql([
32
42
  { id: 1, title: 'Finish ROM', name: 'important' },
33
43
  { id: 2, title: 'Go to sleep', name: nil }
34
44
  ])
35
45
 
36
- expect(tasks.by_tag("important").to_a).to eql([
46
+ expect(tasks.all.with_tags_and_tag_id.to_a).to eql([
47
+ { id: 1, title: 'Finish ROM', tag_id: 1, name: 'important' },
48
+ { id: 2, title: 'Go to sleep', tag_id: nil, name: nil }
49
+ ])
50
+
51
+ expect(tasks.all.by_tag("important").to_a).to eql([
37
52
  { id: 1, title: 'Finish ROM', name: 'important' }
38
53
  ])
39
54
 
@@ -8,7 +8,7 @@ describe 'Defining many-to-one association' do
8
8
  many_to_one :users, key: :user_id
9
9
 
10
10
  def all
11
- select(:id, :title).rename(title: :task_title).qualified
11
+ select(:id, :title)
12
12
  end
13
13
 
14
14
  def with_user
@@ -16,12 +16,26 @@ describe 'Defining many-to-one association' do
16
16
  end
17
17
  end
18
18
 
19
+ setup.mappers do
20
+ define(:tasks)
21
+
22
+ define(:with_user, parent: :tasks) do
23
+ wrap :user do
24
+ attribute :name
25
+ end
26
+ end
27
+ end
28
+
19
29
  setup.relation(:users)
20
30
 
21
31
  tasks = rom.relations.tasks
22
32
 
23
33
  expect(tasks.all.with_user.to_a).to eql(
24
- [{ id: 1, user_name: 'Piotr', task_title: 'Finish ROM' }]
34
+ [{ id: 1, name: 'Piotr', title: 'Finish ROM' }]
35
+ )
36
+
37
+ expect(rom.read(:tasks).all.with_user.to_a).to eql(
38
+ [{ id: 1, title: 'Finish ROM', user: { name: 'Piotr' } }]
25
39
  )
26
40
  end
27
41
  end
@@ -14,14 +14,30 @@ describe 'Defining one-to-many association' do
14
14
  end
15
15
 
16
16
  def with_tasks
17
- association_join(:tasks)
17
+ association_left_join(:tasks, select: [:id, :title])
18
+ end
19
+
20
+ def all
21
+ select(:id, :name)
22
+ end
23
+ end
24
+
25
+ setup.mappers do
26
+ define(:users)
27
+
28
+ define(:with_tasks, parent: :users) do
29
+ group tasks: [:tasks_id, :title]
18
30
  end
19
31
  end
20
32
 
21
33
  users = rom.relations.users
22
34
 
23
35
  expect(users.with_tasks.by_name("Piotr").to_a).to eql(
24
- [{ id: 1, user_id: 1, name: 'Piotr', title: 'Finish ROM' }]
36
+ [{ id: 1, name: 'Piotr', tasks_id: 1, title: 'Finish ROM' }]
37
+ )
38
+
39
+ expect(rom.read(:users).all.with_tasks.by_name("Piotr").to_a).to eql(
40
+ [{ id: 1, name: 'Piotr', tasks: [{ tasks_id: 1, title: 'Finish ROM' }] }]
25
41
  )
26
42
  end
27
43
  end
@@ -26,7 +26,7 @@ describe 'Inferring schema from database' do
26
26
  rom = setup.finalize
27
27
  schema = rom.schema
28
28
 
29
- expect(schema.postgres).to be(nil)
29
+ expect { schema.postgres }.to raise_error(NoMethodError)
30
30
  end
31
31
  end
32
32
  end
metadata CHANGED
@@ -1,95 +1,95 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rom-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-06 00:00:00.000000000 Z
11
+ date: 2014-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '4.17'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.17'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: equalizer
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0.0'
34
- - - ">="
34
+ - - '>='
35
35
  - !ruby/object:Gem::Version
36
36
  version: 0.0.9
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
- - - "~>"
41
+ - - ~>
42
42
  - !ruby/object:Gem::Version
43
43
  version: '0.0'
44
- - - ">="
44
+ - - '>='
45
45
  - !ruby/object:Gem::Version
46
46
  version: 0.0.9
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rom
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - "~>"
51
+ - - ~>
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0.4'
54
- - - ">="
54
+ - - '>='
55
55
  - !ruby/object:Gem::Version
56
56
  version: 0.4.0
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
- - - "~>"
61
+ - - ~>
62
62
  - !ruby/object:Gem::Version
63
63
  version: '0.4'
64
- - - ">="
64
+ - - '>='
65
65
  - !ruby/object:Gem::Version
66
66
  version: 0.4.0
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: bundler
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
- - - ">="
71
+ - - '>='
72
72
  - !ruby/object:Gem::Version
73
73
  version: '0'
74
74
  type: :development
75
75
  prerelease: false
76
76
  version_requirements: !ruby/object:Gem::Requirement
77
77
  requirements:
78
- - - ">="
78
+ - - '>='
79
79
  - !ruby/object:Gem::Version
80
80
  version: '0'
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: rake
83
83
  requirement: !ruby/object:Gem::Requirement
84
84
  requirements:
85
- - - "~>"
85
+ - - ~>
86
86
  - !ruby/object:Gem::Version
87
87
  version: '10.0'
88
88
  type: :development
89
89
  prerelease: false
90
90
  version_requirements: !ruby/object:Gem::Requirement
91
91
  requirements:
92
- - - "~>"
92
+ - - ~>
93
93
  - !ruby/object:Gem::Version
94
94
  version: '10.0'
95
95
  description: RDBMS support for ROM
@@ -99,9 +99,9 @@ executables: []
99
99
  extensions: []
100
100
  extra_rdoc_files: []
101
101
  files:
102
- - ".gitignore"
103
- - ".rspec"
104
- - ".travis.yml"
102
+ - .gitignore
103
+ - .rspec
104
+ - .travis.yml
105
105
  - CHANGELOG.md
106
106
  - Gemfile
107
107
  - LICENSE.txt
@@ -112,7 +112,6 @@ files:
112
112
  - lib/rom/sql/adapter.rb
113
113
  - lib/rom/sql/commands.rb
114
114
  - lib/rom/sql/header.rb
115
- - lib/rom/sql/relation_extension.rb
116
115
  - lib/rom/sql/relation_inclusion.rb
117
116
  - lib/rom/sql/spec/support.rb
118
117
  - lib/rom/sql/support/active_support_notifications.rb
@@ -147,17 +146,17 @@ require_paths:
147
146
  - lib
148
147
  required_ruby_version: !ruby/object:Gem::Requirement
149
148
  requirements:
150
- - - ">="
149
+ - - '>='
151
150
  - !ruby/object:Gem::Version
152
151
  version: '0'
153
152
  required_rubygems_version: !ruby/object:Gem::Requirement
154
153
  requirements:
155
- - - ">="
154
+ - - '>='
156
155
  - !ruby/object:Gem::Version
157
156
  version: '0'
158
157
  requirements: []
159
158
  rubyforge_project:
160
- rubygems_version: 2.2.2
159
+ rubygems_version: 2.0.14
161
160
  signing_key:
162
161
  specification_version: 4
163
162
  summary: RDBMS support for ROM
@@ -1,15 +0,0 @@
1
- module ROM
2
- module SQL
3
-
4
- module RelationExtension
5
- attr_reader :model
6
-
7
- def self.extended(relation)
8
- relation.model.set_dataset(relation.dataset)
9
- relation.model.dataset.naked!
10
- end
11
-
12
- end
13
-
14
- end
15
- end