rom-sql 1.0.0.rc1 → 1.0.0.rc2

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: 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