rom-sql 1.0.0.rc1 → 1.0.0.rc2

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: 7a382cb7f94949a99c91fd0a25edec94612b15da
4
- data.tar.gz: b87bb54a9ae16bbcd2ac87bf416a80582a4df16e
3
+ metadata.gz: 93ca7d5248744905dadd655401fb8e9534b466d2
4
+ data.tar.gz: 0712f79b9575ff7da7017d5bbf7329daae35dc3b
5
5
  SHA512:
6
- metadata.gz: 45f97617413eac1f6c5bb3cf88cd313b74769b9c3e01f5a43939f90e0b48a2f72925b7043b74deeb7c83c9b5cdcf546aa3e347c81b970e521564c13f64c4067b
7
- data.tar.gz: bfeab37bc67d5ed98e3bb75340ab8df8711653146d9f641d1092aef4c42433c974b32aa7af9bc8198a2df16b034523e6e50ac360df7d2890ecf7a15dbee3acd4
6
+ metadata.gz: 2c8c09bf8ec81e1f6c5452c79047ef46529e5fbb9aa8b2c017797120d6c6ef74d2e8ce279fb490d9844a640bc4a117aa0a75d2ef428d0aeba20b79244288c358
7
+ data.tar.gz: fac22ae5b662fbc8bc4bf7529a84efcb9bb0328a62ebd5ab91ca8e0662aaf4697cdfc6b40936211e26d7806bab8e9d717d83beb7a5d19ac5e044300ec7072f26
data/Gemfile CHANGED
@@ -25,3 +25,7 @@ group :test do
25
25
  gem 'sqlite3', platforms: [:mri, :rbx]
26
26
  gem 'jdbc-sqlite3', platforms: :jruby
27
27
  end
28
+
29
+ group :tools do
30
+ gem 'rom-repository', git: 'https://github.com/rom-rb/rom-repository.git', branch: 'master'
31
+ end
@@ -46,7 +46,10 @@ module ROM
46
46
 
47
47
  # @api private
48
48
  def preload(source_key, target_key, source)
49
- where(target_key => source.pluck(source_key.to_sym))
49
+ target_pks = source.pluck(source_key.to_sym).uniq
50
+ target_pks.uniq!
51
+
52
+ where(target_key => target_pks)
50
53
  end
51
54
  end
52
55
  end
@@ -64,9 +64,14 @@ module ROM
64
64
  def associate(relations, children, parent)
65
65
  ((spk, sfk), (tfk, tpk)) = join_key_map(relations)
66
66
 
67
- children.map { |tuple|
68
- { sfk => tuple.fetch(spk), tfk => parent.fetch(tpk) }
69
- }
67
+ case parent
68
+ when Array
69
+ parent.map { |p| associate(relations, children, p) }.flatten(1)
70
+ else
71
+ children.map { |tuple|
72
+ { sfk => tuple.fetch(spk), tfk => parent.fetch(tpk) }
73
+ }
74
+ end
70
75
  end
71
76
 
72
77
  # @api private
@@ -6,24 +6,26 @@ module ROM
6
6
  #
7
7
  # @api public
8
8
  class Attribute < ROM::Schema::Attribute
9
+ # Error raised when an attribute cannot be qualified
9
10
  QualifyError = Class.new(StandardError)
10
11
 
11
- # Return a new type marked as a FK
12
+ # Return a new attribute with an alias
13
+ #
14
+ # @example
15
+ # users[:id].aliased(:user_id)
12
16
  #
13
17
  # @return [SQL::Attribute]
14
18
  #
15
- # @api public
16
- def foreign_key
17
- meta(foreign_key: true)
18
- end
19
-
20
19
  # @api public
21
20
  def aliased(name)
22
21
  super.meta(sql_expr: sql_expr.as(name))
23
22
  end
24
23
  alias_method :as, :aliased
25
24
 
26
- # Return a new type marked as qualified
25
+ # Return a new attribute marked as qualified
26
+ #
27
+ # @example
28
+ # users[:id].aliased(:user_id)
27
29
  #
28
30
  # @return [SQL::Attribute]
29
31
  #
@@ -40,7 +42,10 @@ module ROM
40
42
  end
41
43
  end
42
44
 
43
- # Return a new type marked as joined
45
+ # Return a new attribute marked as joined
46
+ #
47
+ # Whenever you join two schemas, the right schema's attribute
48
+ # will be marked as joined using this method
44
49
  #
45
50
  # @return [SQL::Attribute]
46
51
  #
@@ -51,6 +56,12 @@ module ROM
51
56
 
52
57
  # Return if an attribute was used in a join
53
58
  #
59
+ # @example
60
+ # schema = users.schema.join(tasks.schema)
61
+ #
62
+ # schema[:id, :tasks].joined?
63
+ # # => true
64
+ #
54
65
  # @return [Boolean]
55
66
  #
56
67
  # @api public
@@ -60,6 +71,12 @@ module ROM
60
71
 
61
72
  # Return if an attribute type is qualified
62
73
  #
74
+ # @example
75
+ # id = users[:id].qualify
76
+ #
77
+ # id.qualified?
78
+ # # => true
79
+ #
63
80
  # @return [Boolean]
64
81
  #
65
82
  # @api public
@@ -67,6 +84,32 @@ module ROM
67
84
  meta[:qualified].equal?(true)
68
85
  end
69
86
 
87
+ # Return a new attribute marked as a FK
88
+ #
89
+ # @return [SQL::Attribute]
90
+ #
91
+ # @api public
92
+ def foreign_key
93
+ meta(foreign_key: true)
94
+ end
95
+
96
+ # Return symbol representation of an attribute
97
+ #
98
+ # This uses convention from sequel where double underscore in the name
99
+ # is used for qualifying, and triple underscore means aliasing
100
+ #
101
+ # @example
102
+ # users[:id].qualified.to_sym
103
+ # # => :users__id
104
+ #
105
+ # users[:id].as(:user_id).to_sym
106
+ # # => :id___user_id
107
+ #
108
+ # users[:id].qualified.as(:user_id).to_sym
109
+ # # => :users__id___user_id
110
+ #
111
+ # @return [Symbol]
112
+ #
70
113
  # @api public
71
114
  def to_sym
72
115
  @_to_sym ||=
@@ -81,6 +124,10 @@ module ROM
81
124
  end
82
125
  end
83
126
 
127
+ # Sequel calls this method to coerce an attribute into SQL string
128
+ #
129
+ # @param [Sequel::Dataset]
130
+ #
84
131
  # @api private
85
132
  def sql_literal(ds)
86
133
  if sql_expr
@@ -92,11 +139,15 @@ module ROM
92
139
 
93
140
  private
94
141
 
142
+ # Return Sequel Expression object for an attribute
143
+ #
95
144
  # @api private
96
145
  def sql_expr
97
146
  @sql_expr ||= (meta[:sql_expr] || Sequel[to_sym])
98
147
  end
99
148
 
149
+ # Delegate to sql expression if it responds to a given method
150
+ #
100
151
  # @api private
101
152
  def method_missing(meth, *args, &block)
102
153
  if sql_expr.respond_to?(meth)
@@ -95,7 +95,7 @@ module ROM
95
95
  upsert(input[tuple], upsert_options)
96
96
  end
97
97
 
98
- inserted_tuples.flatten
98
+ inserted_tuples.flatten(1)
99
99
  end
100
100
 
101
101
  # @api private
@@ -45,7 +45,9 @@ module ROM
45
45
  #
46
46
  # @api public
47
47
  def associate(tuples, parent, assoc:, keys:)
48
- input_tuples =
48
+ result_type = result
49
+
50
+ output_tuples =
49
51
  case assoc
50
52
  when Symbol
51
53
  fk, pk = keys
@@ -54,6 +56,8 @@ module ROM
54
56
  tuple.merge(fk => parent.fetch(pk))
55
57
  }
56
58
  when Association::ManyToMany
59
+ result_type = tuples.is_a?(Array) ? :many : :one
60
+
57
61
  join_tuples = assoc.associate(__registry__, tuples, parent)
58
62
  join_relation = assoc.join_relation(__registry__)
59
63
  join_relation.multi_insert(join_tuples)
@@ -62,16 +66,21 @@ module ROM
62
66
  .associations[assoc.source]
63
67
  .combine_keys(__registry__).to_a.flatten
64
68
 
65
- pk_extend = { fk => parent[pk] }
66
-
67
- tuples.map { |tuple| tuple.update(pk_extend) }
69
+ case parent
70
+ when Array
71
+ parent.map do |p|
72
+ tuples.map { |tuple| tuple.merge(fk => p[pk]) }
73
+ end.flatten(1)
74
+ else
75
+ tuples.map { |tuple| Hash(tuple).update(fk => parent[pk]) }
76
+ end
68
77
  when Association
69
78
  with_input_tuples(tuples).map { |tuple|
70
79
  assoc.associate(relation.__registry__, tuple, parent)
71
80
  }
72
81
  end
73
82
 
74
- one? ? input_tuples[0] : input_tuples
83
+ result_type == :one ? output_tuples[0] : output_tuples
75
84
  end
76
85
 
77
86
  # @api public
@@ -40,6 +40,7 @@ module ROM
40
40
  begin
41
41
  inferrer_for_db.new.call(name, gateway)
42
42
  rescue Sequel::Error => e
43
+ inferrer_for_db.on_error(klass, e)
43
44
  ROM::Schema::DEFAULT_INFERRER.()
44
45
  end
45
46
  end
@@ -42,6 +42,14 @@ module ROM
42
42
  db_registry[type]
43
43
  end
44
44
 
45
+ def self.on_error(relation, e)
46
+ warn "[#{relation}] failed to infer schema. " \
47
+ "Make sure tables exist before ROM container is set up. " \
48
+ "This may also happen when your migration tasks load ROM container, " \
49
+ "which is not needed for migrations as only the connection is required " \
50
+ "(#{e.message})"
51
+ end
52
+
45
53
  # @api private
46
54
  def call(source, gateway)
47
55
  dataset = source.dataset
data/lib/rom/sql/types.rb CHANGED
@@ -9,7 +9,7 @@ module ROM
9
9
  ROM::Types.Constructor(*args, &block)
10
10
  end
11
11
 
12
- Serial = Int.constrained(gt: 0).meta(primary_key: true)
12
+ Serial = Int.meta(primary_key: true)
13
13
 
14
14
  Blob = Constructor(Sequel::SQL::Blob, &Sequel::SQL::Blob.method(:new))
15
15
  end
@@ -1,5 +1,5 @@
1
1
  module ROM
2
2
  module SQL
3
- VERSION = '1.0.0.rc1'.freeze
3
+ VERSION = '1.0.0.rc2'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,69 @@
1
+ RSpec.describe 'Plugins / :associates / with many-to-many', :sqlite do
2
+ include_context 'database setup'
3
+
4
+ let(:tasks) { container.commands[:tasks] }
5
+ let(:tags) { container.commands[:tags] }
6
+
7
+ let(:jane) do
8
+ relations[:users].by_pk(relations[:users].insert(name: 'Jane')).one
9
+ end
10
+
11
+ let(:john) do
12
+ relations[:users].by_pk(relations[:users].insert(name: 'John')).one
13
+ end
14
+
15
+ before do
16
+ conf.relation(:tasks) do
17
+ schema(infer: true) do
18
+ associations do
19
+ has_many :tags, through: :task_tags
20
+ end
21
+ end
22
+ end
23
+
24
+ conf.relation(:task_tags) do
25
+ schema(infer: true) do
26
+ associations do
27
+ belongs_to :tasks, as: :task
28
+ belongs_to :tags, as: :tag
29
+ end
30
+ end
31
+ end
32
+
33
+ conf.relation(:tags) do
34
+ schema(infer: true) do
35
+ associations do
36
+ has_many :tasks, through: :task_tags
37
+ end
38
+ end
39
+ end
40
+
41
+ conf.commands(:tags) do
42
+ define(:create) do
43
+ result :many
44
+ end
45
+ end
46
+
47
+ conf.commands(:tasks) do
48
+ define(:create) do
49
+ result :many
50
+ associates :tags
51
+ end
52
+ end
53
+ end
54
+
55
+ it 'associates a child with many parents' do
56
+ create_tags = tags[:create].with([{ name: 'red' }, { name: 'blue' }])
57
+ create_task = tasks[:create].with(user_id: jane[:id], title: "Jade's task")
58
+
59
+ command = create_tags >> create_task
60
+
61
+ result = command.call
62
+
63
+ expect(result).
64
+ to eql([
65
+ { id: 1, user_id: jane[:id], title: "Jade's task", tag_id: 1 },
66
+ { id: 1, user_id: jane[:id], title: "Jade's task", tag_id: 2 }
67
+ ])
68
+ end
69
+ end
data/spec/spec_helper.rb CHANGED
@@ -39,6 +39,10 @@ ADAPTERS = DB_URIS.keys
39
39
  PG_LTE_95 = ENV.fetch('PG_LTE_95', 'true') == 'true'
40
40
 
41
41
  SPEC_ROOT = root = Pathname(__FILE__).dirname
42
+
43
+ # Redirect inferrer warnings to a log file
44
+ $stderr.reopen(SPEC_ROOT.join('../log/warnings.log'), 'w')
45
+
42
46
  TMP_PATH = root.join('../tmp')
43
47
 
44
48
  Dir[root.join('shared/**/*')].each { |f| require f }
@@ -1,16 +1,6 @@
1
1
  require 'rom/sql/types'
2
2
 
3
3
  RSpec.describe ROM::SQL::Types, :postgres do
4
- describe 'ROM::SQL::Types::Serial' do
5
- it 'accepts ints > 0' do
6
- expect(ROM::SQL::Types::Serial[1]).to be(1)
7
- end
8
-
9
- it 'raises when input is <= 0' do
10
- expect { ROM::SQL::Types::Serial[0] }.to raise_error(Dry::Types::ConstraintError)
11
- end
12
- end
13
-
14
4
  describe ROM::SQL::Types::Blob do
15
5
  it 'coerces strings to Sequel::SQL::Blob' do
16
6
  input = 'sutin'
metadata CHANGED
@@ -1,44 +1,45 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rom-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc1
4
+ version: 1.0.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-24 00:00:00.000000000 Z
11
+ date: 2017-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
+ name: sequel
14
15
  requirement: !ruby/object:Gem::Requirement
15
16
  requirements:
16
17
  - - "~>"
17
18
  - !ruby/object:Gem::Version
18
19
  version: '4.42'
19
- name: sequel
20
- prerelease: false
21
20
  type: :runtime
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.42'
27
27
  - !ruby/object:Gem::Dependency
28
+ name: dry-equalizer
28
29
  requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
31
  - - "~>"
31
32
  - !ruby/object:Gem::Version
32
33
  version: '0.2'
33
- name: dry-equalizer
34
- prerelease: false
35
34
  type: :runtime
35
+ prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.2'
41
41
  - !ruby/object:Gem::Dependency
42
+ name: dry-types
42
43
  requirement: !ruby/object:Gem::Requirement
43
44
  requirements:
44
45
  - - "~>"
@@ -47,9 +48,8 @@ dependencies:
47
48
  - - ">="
48
49
  - !ruby/object:Gem::Version
49
50
  version: 0.9.4
50
- name: dry-types
51
- prerelease: false
52
51
  type: :runtime
52
+ prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - "~>"
@@ -59,6 +59,7 @@ dependencies:
59
59
  - !ruby/object:Gem::Version
60
60
  version: 0.9.4
61
61
  - !ruby/object:Gem::Dependency
62
+ name: dry-core
62
63
  requirement: !ruby/object:Gem::Requirement
63
64
  requirements:
64
65
  - - "~>"
@@ -67,9 +68,8 @@ dependencies:
67
68
  - - ">="
68
69
  - !ruby/object:Gem::Version
69
70
  version: 0.2.3
70
- name: dry-core
71
- prerelease: false
72
71
  type: :runtime
72
+ prerelease: false
73
73
  version_requirements: !ruby/object:Gem::Requirement
74
74
  requirements:
75
75
  - - "~>"
@@ -79,42 +79,42 @@ dependencies:
79
79
  - !ruby/object:Gem::Version
80
80
  version: 0.2.3
81
81
  - !ruby/object:Gem::Dependency
82
+ name: rom
82
83
  requirement: !ruby/object:Gem::Requirement
83
84
  requirements:
84
85
  - - "~>"
85
86
  - !ruby/object:Gem::Version
86
87
  version: 3.0.0.rc
87
- name: rom
88
- prerelease: false
89
88
  type: :runtime
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: 3.0.0.rc
95
95
  - !ruby/object:Gem::Dependency
96
+ name: bundler
96
97
  requirement: !ruby/object:Gem::Requirement
97
98
  requirements:
98
99
  - - ">="
99
100
  - !ruby/object:Gem::Version
100
101
  version: '0'
101
- name: bundler
102
- prerelease: false
103
102
  type: :development
103
+ prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - ">="
107
107
  - !ruby/object:Gem::Version
108
108
  version: '0'
109
109
  - !ruby/object:Gem::Dependency
110
+ name: rake
110
111
  requirement: !ruby/object:Gem::Requirement
111
112
  requirements:
112
113
  - - "~>"
113
114
  - !ruby/object:Gem::Version
114
115
  version: '10.0'
115
- name: rake
116
- prerelease: false
117
116
  type: :development
117
+ prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - "~>"
@@ -224,6 +224,7 @@ files:
224
224
  - spec/integration/commands/upsert_spec.rb
225
225
  - spec/integration/gateway_spec.rb
226
226
  - spec/integration/migration_spec.rb
227
+ - spec/integration/plugins/associates/many_to_many_spec.rb
227
228
  - spec/integration/plugins/associates_spec.rb
228
229
  - spec/integration/plugins/auto_wrap_spec.rb
229
230
  - spec/integration/relation_schema_spec.rb
@@ -299,7 +300,7 @@ homepage: http://rom-rb.org
299
300
  licenses:
300
301
  - MIT
301
302
  metadata: {}
302
- post_install_message:
303
+ post_install_message:
303
304
  rdoc_options: []
304
305
  require_paths:
305
306
  - lib
@@ -314,9 +315,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
314
315
  - !ruby/object:Gem::Version
315
316
  version: 1.3.1
316
317
  requirements: []
317
- rubyforge_project:
318
- rubygems_version: 2.6.8
319
- signing_key:
318
+ rubyforge_project:
319
+ rubygems_version: 2.5.1
320
+ signing_key:
320
321
  specification_version: 4
321
322
  summary: SQL databases support for ROM
322
323
  test_files:
@@ -344,6 +345,7 @@ test_files:
344
345
  - spec/integration/commands/upsert_spec.rb
345
346
  - spec/integration/gateway_spec.rb
346
347
  - spec/integration/migration_spec.rb
348
+ - spec/integration/plugins/associates/many_to_many_spec.rb
347
349
  - spec/integration/plugins/associates_spec.rb
348
350
  - spec/integration/plugins/auto_wrap_spec.rb
349
351
  - spec/integration/relation_schema_spec.rb