rom-sql 0.6.1 → 0.7.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -5
  3. data/.rubocop_todo.yml +12 -6
  4. data/.travis.yml +3 -2
  5. data/CHANGELOG.md +27 -0
  6. data/Gemfile +1 -0
  7. data/lib/rom/plugins/relation/sql/auto_combine.rb +45 -0
  8. data/lib/rom/plugins/relation/sql/auto_wrap.rb +48 -0
  9. data/lib/rom/plugins/relation/sql/base_view.rb +31 -0
  10. data/lib/rom/sql.rb +17 -10
  11. data/lib/rom/sql/commands/error_wrapper.rb +1 -1
  12. data/lib/rom/sql/commands/update.rb +1 -1
  13. data/lib/rom/sql/gateway.rb +8 -2
  14. data/lib/rom/sql/header.rb +1 -1
  15. data/lib/rom/sql/migration.rb +11 -20
  16. data/lib/rom/sql/migration/migrator.rb +4 -0
  17. data/lib/rom/sql/{relation/associations.rb → plugin/assoc_macros.rb} +17 -2
  18. data/lib/rom/sql/plugin/assoc_macros/class_interface.rb +128 -0
  19. data/lib/rom/sql/plugin/associates.rb +2 -4
  20. data/lib/rom/sql/plugin/pagination.rb +1 -1
  21. data/lib/rom/sql/plugins.rb +4 -2
  22. data/lib/rom/sql/relation.rb +46 -6
  23. data/lib/rom/sql/relation/reading.rb +63 -0
  24. data/lib/rom/sql/tasks/migration_tasks.rake +37 -16
  25. data/lib/rom/sql/version.rb +1 -1
  26. data/rom-sql.gemspec +2 -2
  27. data/spec/fixtures/migrations/20150403090603_create_carrots.rb +1 -1
  28. data/spec/integration/combine_spec.rb +6 -6
  29. data/spec/integration/commands/create_spec.rb +25 -25
  30. data/spec/integration/commands/delete_spec.rb +6 -6
  31. data/spec/integration/commands/update_spec.rb +5 -5
  32. data/spec/integration/{repository_spec.rb → gateway_spec.rb} +34 -21
  33. data/spec/integration/migration_spec.rb +3 -3
  34. data/spec/integration/read_spec.rb +13 -7
  35. data/spec/shared/database_setup.rb +3 -5
  36. data/spec/spec_helper.rb +3 -6
  37. data/spec/support/active_support_notifications_spec.rb +1 -1
  38. data/spec/support/rails_log_subscriber_spec.rb +1 -1
  39. data/spec/unit/association_errors_spec.rb +4 -3
  40. data/spec/unit/combined_associations_spec.rb +7 -5
  41. data/spec/unit/gateway_spec.rb +2 -2
  42. data/spec/unit/logger_spec.rb +1 -1
  43. data/spec/unit/many_to_many_spec.rb +7 -4
  44. data/spec/unit/many_to_one_spec.rb +14 -8
  45. data/spec/unit/migration_tasks_spec.rb +7 -6
  46. data/spec/unit/one_to_many_spec.rb +16 -10
  47. data/spec/unit/plugin/base_view_spec.rb +18 -0
  48. data/spec/unit/plugin/pagination_spec.rb +10 -10
  49. data/spec/unit/relation_spec.rb +69 -3
  50. data/spec/unit/schema_spec.rb +5 -3
  51. metadata +19 -26
  52. data/lib/rom/sql/relation/class_methods.rb +0 -116
  53. data/lib/rom/sql/relation/inspection.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 092d7392edf6c7ee3ab5ae94c46642226cc9fe9c
4
- data.tar.gz: 53c8bcf25ba8f8855b06e8e9f17e26c06c2ec359
3
+ metadata.gz: 46efc98cee0cbb717be8ea7e8be52c08a488957c
4
+ data.tar.gz: 7b170fa8ac4f6c827c0feb6bd66e475322d28606
5
5
  SHA512:
6
- metadata.gz: 07fd1f32d6a6d81068434ed420be539ab0a6781b24cb1601dbffc9c3620d29b2b02201f90cb121f4d33afd03e91faa8f07c47e687af2ff45a9ef57e3d889a749
7
- data.tar.gz: 4176a62ba46b000a5248a76cb06e355440e46532c9050199533add0e9c4a3009d56c9e5a137edc70c402ea2a092c68fdc81de2f6f00f817194beb300289447df
6
+ metadata.gz: 3b2af71d8e33ab121d77365e7c743fa33817ea24daa67f2c87a3b4f1610a97c601482e0147ea77e3bb21fde6180c1e8c21c693f3e0ba9bc0eed0ad7d0221133a
7
+ data.tar.gz: 3eb3431b48c7739ffdbaf40cbc4a4ed33cef80ddcdfedfd0e3cc89928bb9dbdb4a194aef8626244aedaf7e56ceb952456cee3174660d2b36e48b8b75fccaad3c
@@ -29,7 +29,7 @@ Style/AsciiComments:
29
29
  Enabled: false
30
30
 
31
31
  # Allow using braces for value-returning blocks
32
- Style/Blocks:
32
+ Style/BlockDelimiters:
33
33
  Enabled: false
34
34
 
35
35
  # Documentation checked by Inch CI
@@ -53,10 +53,6 @@ Style/OpMethod:
53
53
  Exclude:
54
54
  - lib/rom/command_registry.rb
55
55
 
56
- # Even a single escaped slash can be confusing
57
- Style/RegexpLiteral:
58
- MaxSlashes: 0
59
-
60
56
  # Don’t introduce semantic fail/raise distinction
61
57
  Style/SignalException:
62
58
  EnforcedStyle: only_raise
@@ -1,15 +1,21 @@
1
- # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2015-01-25 22:37:25 +0100 using RuboCop version 0.28.0.
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2015-09-26 17:45:40 +0200 using RuboCop version 0.34.2.
3
4
  # The point is for the user to remove these configuration records
4
5
  # one by one as the offenses are removed from the code base.
5
6
  # Note that changes in the inspected code, or installation of new
6
7
  # versions of RuboCop, may require this file to be generated again.
7
8
 
8
- # Offense count: 3
9
+ # Offense count: 4
9
10
  Metrics/AbcSize:
10
- Max: 20
11
+ Max: 23
11
12
 
12
- # Offense count: 2
13
+ # Offense count: 17
14
+ # Configuration parameters: AllowURI, URISchemes.
15
+ Metrics/LineLength:
16
+ Max: 118
17
+
18
+ # Offense count: 5
13
19
  # Configuration parameters: CountComments.
14
20
  Metrics/MethodLength:
15
- Max: 14
21
+ Max: 17
@@ -3,14 +3,14 @@ sudo: false
3
3
  cache: bundler
4
4
  bundler_args: --without yard guard benchmarks tools
5
5
  before_script:
6
- - psql -c 'create database rom;' -U postgres
6
+ - psql -c 'create database rom_sql;' -U postgres
7
7
  script: "bundle exec rake ci"
8
8
  rvm:
9
9
  - 2.0
10
10
  - 2.1
11
11
  - 2.2
12
12
  - rbx-2
13
- - jruby
13
+ - jruby-9000
14
14
  - jruby-head
15
15
  - ruby-head
16
16
  env:
@@ -19,6 +19,7 @@ env:
19
19
  - JRUBY_OPTS='--dev -J-Xmx1024M'
20
20
  matrix:
21
21
  allow_failures:
22
+ - rvm: jruby-9000
22
23
  - rvm: ruby-head
23
24
  - rvm: jruby-head
24
25
  notifications:
@@ -1,3 +1,30 @@
1
+ ## v0.7.0 to-be-released
2
+
3
+ ### Added
4
+
5
+ * Repository plugins have been imported:
6
+ * `view` allows defining a relation view with an explicit header (solnic)
7
+ * `base_view` defines a base view with all column names as the header (solnic)
8
+ * `auto_combine` defines a generic `for_combine` method which eager-loads
9
+ parent/children relation (solnic)
10
+ * `auto-wrap` defines a generic `for_wrap` method which inner-joins
11
+ a parent/children relation (solnic)
12
+ * Possibility to check for pending migrations (gotar)
13
+ * `Relation#sum` interface (gotar)
14
+ * `Relation#avg` interface (gotar)
15
+ * `Relation#min` interface (gotar)
16
+ * `Relation#max` interface (gotar)
17
+ * `Relation#union` interface (spscream)
18
+ * `primary_key` macro which allows setting a custom primary key when it cannot be
19
+ inferred automatically (solnic)
20
+
21
+ ### Changed
22
+
23
+ * `ROM::SQL.gateway` renamed to `ROM::SQL::Gateway.instance` for migrations (endash)
24
+ * Association macros are now an optional plugin (solnic)
25
+
26
+ [Compare v0.6.1...HEAD](https://github.com/rom-rb/rom-sql/compare/v0.6.1...HEAD)
27
+
1
28
  ## v0.6.1 2015-09-23
2
29
 
3
30
  ### Added
data/Gemfile CHANGED
@@ -5,6 +5,7 @@ gemspec
5
5
  group :test do
6
6
  gem 'byebug', platforms: :mri
7
7
  gem 'anima', '~> 0.2.0'
8
+ gem 'transproc', github: 'solnic/transproc', branch: 'master'
8
9
  gem 'rom', github: 'rom-rb/rom', branch: 'master'
9
10
  gem 'rom-support', github: 'rom-rb/rom-support', branch: 'master'
10
11
  gem 'rom-mapper', github: 'rom-rb/rom-mapper', branch: 'master'
@@ -0,0 +1,45 @@
1
+ module ROM
2
+ module Plugins
3
+ module Relation
4
+ module SQL
5
+ module AutoCombine
6
+ # @api private
7
+ def self.included(klass)
8
+ super
9
+ klass.class_eval do
10
+ include(InstanceInterface)
11
+ extend(ClassInterface)
12
+ end
13
+ end
14
+
15
+ module ClassInterface
16
+ def inherited(klass)
17
+ super
18
+ klass.auto_curry :for_combine
19
+ end
20
+ end
21
+
22
+ module InstanceInterface
23
+ # Default methods for fetching combined relation
24
+ #
25
+ # This method is used by default by `combine`
26
+ #
27
+ # @return [SQL::Relation]
28
+ #
29
+ # @api private
30
+ def for_combine(keys, relation)
31
+ pk, fk = keys.to_a.flatten
32
+ where(fk => relation.map { |tuple| tuple[pk] })
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ ROM.plugins do
42
+ adapter :sql do
43
+ register :auto_combine, ROM::Plugins::Relation::SQL::AutoCombine, type: :relation
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ module ROM
2
+ module Plugins
3
+ module Relation
4
+ module SQL
5
+ module AutoWrap
6
+ # @api private
7
+ def self.included(klass)
8
+ super
9
+ klass.class_eval do
10
+ include(InstanceInterface)
11
+ extend(ClassInterface)
12
+ end
13
+ end
14
+
15
+ module ClassInterface
16
+ def inherited(klass)
17
+ super
18
+ klass.auto_curry :for_wrap
19
+ end
20
+ end
21
+
22
+ module InstanceInterface
23
+ # Default methods for fetching wrapped relation
24
+ #
25
+ # This method is used by default by `wrap` and `wrap_parents`
26
+ #
27
+ # @return [SQL::Relation]
28
+ #
29
+ # @api private
30
+ def for_wrap(keys, name)
31
+ other = __registry__[name]
32
+
33
+ inner_join(name, keys)
34
+ .select(*qualified.header.columns)
35
+ .select_append(*other.prefix(other.name).qualified.header)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ ROM.plugins do
45
+ adapter :sql do
46
+ register :auto_wrap, ROM::Plugins::Relation::SQL::AutoWrap, type: :relation
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ module ROM
2
+ module Plugins
3
+ module Relation
4
+ module SQL
5
+ module BaseView
6
+ # @api private
7
+ def self.included(klass)
8
+ super
9
+ klass.extend(ClassInterface)
10
+ end
11
+
12
+ module ClassInterface
13
+ def inherited(klass)
14
+ super
15
+ klass.view(:base) do
16
+ header { dataset.columns }
17
+ relation { select(*attributes(:base)).order(primary_key) }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ ROM.plugins do
28
+ adapter :sql do
29
+ register :base_view, ROM::Plugins::Relation::SQL::BaseView, type: :relation
30
+ end
31
+ end
@@ -1,16 +1,23 @@
1
- require "sequel"
2
- require "rom"
1
+ require 'dry-equalizer'
2
+ require 'sequel'
3
+ require 'rom'
3
4
 
4
- require "rom/sql/version"
5
- require "rom/sql/errors"
6
- require "rom/sql/plugins"
7
- require "rom/sql/relation"
8
- require "rom/sql/gateway"
9
- require "rom/sql/migration"
5
+ module ROM
6
+ MissingConfigurationError = Class.new(StandardError)
7
+ end
8
+
9
+ require 'rom/configuration_dsl'
10
+
11
+ require 'rom/sql/version'
12
+ require 'rom/sql/errors'
13
+ require 'rom/sql/plugins'
14
+ require 'rom/sql/relation'
15
+ require 'rom/sql/gateway'
16
+ require 'rom/sql/migration'
10
17
 
11
18
  if defined?(Rails)
12
- require "rom/sql/support/active_support_notifications"
13
- require "rom/sql/support/rails_log_subscriber"
19
+ require 'rom/sql/support/active_support_notifications'
20
+ require 'rom/sql/support/rails_log_subscriber'
14
21
  end
15
22
 
16
23
  ROM.register_adapter(:sql, ROM::SQL)
@@ -14,7 +14,7 @@ module ROM
14
14
  raise ERROR_MAP[e.class], e
15
15
  end
16
16
 
17
- alias :[] :call
17
+ alias_method :[], :call
18
18
  end
19
19
  end
20
20
  end
@@ -13,7 +13,7 @@ module ROM
13
13
  class Update < ROM::Commands::Update
14
14
  adapter :sql
15
15
 
16
- include Deprecations
16
+ extend Deprecations
17
17
 
18
18
  include Transaction
19
19
  include ErrorWrapper
@@ -19,6 +19,10 @@ module ROM
19
19
  include Options
20
20
  include Migration
21
21
 
22
+ class << self
23
+ attr_accessor :instance
24
+ end
25
+
22
26
  # Return optionally configured logger
23
27
  #
24
28
  # @return [Object] logger
@@ -63,13 +67,15 @@ module ROM
63
67
  # @api public
64
68
  def initialize(uri, options = {})
65
69
  repo_options = self.class.option_definitions.names
66
- conn_options = options.reject { |k,_| repo_options.include?(k) }
70
+ conn_options = options.reject { |k, _| repo_options.include?(k) }
67
71
 
68
72
  @connection = connect(uri, conn_options)
69
73
  @schema = connection.tables
70
74
  add_extensions(Array(options[:extensions])) if options[:extensions]
71
75
 
72
- super(uri, options.reject { |k,_| conn_options.keys.include?(k) })
76
+ super(uri, options.reject { |k, _| conn_options.keys.include?(k) })
77
+
78
+ self.class.instance = self
73
79
  end
74
80
 
75
81
  # Disconnect from database
@@ -2,7 +2,7 @@ module ROM
2
2
  module SQL
3
3
  # @private
4
4
  class Header
5
- include Equalizer.new(:columns, :table)
5
+ include Dry::Equalizer(:columns, :table)
6
6
 
7
7
  attr_reader :columns, :table
8
8
 
@@ -2,7 +2,8 @@ require 'rom/sql/migration/migrator'
2
2
 
3
3
  module ROM
4
4
  module SQL
5
- # Create a database migration for a specific gateway
5
+ # Trap for the migration runner. To create a migration
6
+ # on a specific gateway, use ROM::SQL::Gateway#migration
6
7
  #
7
8
  # @example
8
9
  # ROM.setup(
@@ -21,26 +22,9 @@ module ROM
21
22
  # end
22
23
  # end
23
24
  #
24
- # # for a non-default gateway
25
- # ROM::SQL.migration(:other) do
26
- # # ...
27
- # end
28
- #
29
25
  # @api public
30
- def self.migration(gateway = :default, &block)
31
- gateways = ROM.boot ? ROM.boot.gateways : ROM.env.gateways
32
- gateways[gateway].migration(&block)
33
- end
34
-
35
- # Return first sql gateway for migrations
36
- #
37
- # This is used by migration tasks, they only support a single sql gateway
38
- #
39
- # @api private
40
- def self.gateway
41
- ROM.gateways
42
- .keys
43
- .detect { |gateway| gateway.instance_of?(Gateway) }
26
+ def self.migration(&block)
27
+ ROM::SQL::Gateway.instance.migration(&block)
44
28
  end
45
29
 
46
30
  module Migration
@@ -55,6 +39,13 @@ module ROM
55
39
  end
56
40
  end
57
41
 
42
+ # @see ROM::SQL::Migration.pending?
43
+ #
44
+ # @api public
45
+ def pending_migrations?
46
+ migrator.pending?
47
+ end
48
+
58
49
  # @see ROM::SQL.migration
59
50
  #
60
51
  # @api public
@@ -20,6 +20,10 @@ module ROM
20
20
  Sequel::Migrator.run(connection, path.to_s, options)
21
21
  end
22
22
 
23
+ def pending?
24
+ !Sequel::Migrator.is_current?(connection, path.to_s)
25
+ end
26
+
23
27
  def migration(&block)
24
28
  Sequel.migration(&block)
25
29
  end
@@ -1,7 +1,22 @@
1
+ require 'rom/sql/plugin/assoc_macros/class_interface'
2
+
1
3
  module ROM
2
4
  module SQL
3
- class Relation < ROM::Relation
4
- module Associations
5
+ module Plugin
6
+ module AssocMacros
7
+ # Extends a relation class with assoc-macros and instance-level methods
8
+ #
9
+ # @api private
10
+ def self.included(relation)
11
+ super
12
+ relation.extend(ClassInterface)
13
+ end
14
+
15
+ # @api private
16
+ def model
17
+ self.class.model
18
+ end
19
+
5
20
  # Join configured association.
6
21
  #
7
22
  # Uses INNER JOIN type.
@@ -0,0 +1,128 @@
1
+ module ROM
2
+ module SQL
3
+ module Plugin
4
+ module AssocMacros
5
+ # Class DSL for SQL relations
6
+ #
7
+ # @api private
8
+ module ClassInterface
9
+ # @api private
10
+ def self.prepare(klass)
11
+ klass.class_eval do
12
+ class << self
13
+ attr_reader :model, :associations
14
+ end
15
+ end
16
+ klass.instance_variable_set('@model', Class.new(Sequel::Model))
17
+ klass.instance_variable_set('@associations', [])
18
+ end
19
+
20
+ # @api private
21
+ def self.extended(klass)
22
+ prepare(klass)
23
+ end
24
+
25
+ # Set up model and association ivars for descendant class
26
+ #
27
+ # @api private
28
+ def inherited(klass)
29
+ super
30
+ ClassInterface.prepare(klass)
31
+ end
32
+
33
+ # Set up a one-to-many association
34
+ #
35
+ # @example
36
+ # class Users < ROM::Relation[:sql]
37
+ # one_to_many :tasks, key: :user_id
38
+ #
39
+ # def with_tasks
40
+ # association_join(:tasks)
41
+ # end
42
+ # end
43
+ #
44
+ # @param [Symbol] name The name of the association
45
+ # @param [Hash] options The options hash
46
+ # @option options [Symbol] :key Name of the key to join on
47
+ # @option options [Hash] :on Additional conditions for join
48
+ # @option options [Hash] :conditions Additional conditions for WHERE
49
+ #
50
+ # @api public
51
+ def one_to_many(name, options)
52
+ associations << [__method__, name, { relation: name }.merge(options)]
53
+ end
54
+
55
+ # Set up a many-to-many association
56
+ #
57
+ # @example
58
+ # class Tasks < ROM::Relation[:sql]
59
+ # many_to_many :tags,
60
+ # join_table: :task_tags,
61
+ # left_key: :task_id,
62
+ # right_key: :tag_id,
63
+ #
64
+ # def with_tags
65
+ # association_join(:tags)
66
+ # end
67
+ # end
68
+ #
69
+ # @param [Symbol] name The name of the association
70
+ # @param [Hash] options The options hash
71
+ # @option options [Symbol] :join_table Name of the join table
72
+ # @option options [Hash] :left_key Name of the left join key
73
+ # @option options [Hash] :right_key Name of the right join key
74
+ # @option options [Hash] :on Additional conditions for join
75
+ # @option options [Hash] :conditions Additional conditions for WHERE
76
+ #
77
+ # @api public
78
+ def many_to_many(name, options = {})
79
+ associations << [__method__, name, { relation: name }.merge(options)]
80
+ end
81
+
82
+ # Set up a many-to-one association
83
+ #
84
+ # @example
85
+ # class Tasks < ROM::Relation[:sql]
86
+ # many_to_one :users, key: :user_id
87
+ #
88
+ # def with_users
89
+ # association_join(:users)
90
+ # end
91
+ # end
92
+ #
93
+ # @param [Symbol] name The name of the association
94
+ # @param [Hash] options The options hash
95
+ # @option options [Symbol] :join_table Name of the join table
96
+ # @option options [Hash] :key Name of the join key
97
+ # @option options [Hash] :on Additional conditions for join
98
+ # @option options [Hash] :conditions Additional conditions for WHERE
99
+ #
100
+ # @api public
101
+ def many_to_one(name, options = {})
102
+ associations << [__method__, name, { relation: name }.merge(options)]
103
+ end
104
+
105
+ # Finalize the relation by setting up its associations (if any)
106
+ #
107
+ # @api private
108
+ def finalize(relations, relation)
109
+ return unless relation.dataset.db.table_exists?(dataset)
110
+
111
+ model.set_dataset(relation.dataset)
112
+ model.dataset.naked!
113
+
114
+ associations.each do |*args, assoc_opts|
115
+ options = Hash[assoc_opts]
116
+ other = relations[options.delete(:relation) || args[1]].model
117
+ model.public_send(*args, options.merge(class: other))
118
+ end
119
+
120
+ model.freeze
121
+
122
+ super
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end