rom 0.5.0 → 0.6.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +19 -15
  3. data/.rubocop_todo.yml +28 -0
  4. data/.travis.yml +8 -1
  5. data/CHANGELOG.md +40 -0
  6. data/Gemfile +10 -2
  7. data/Guardfile +12 -10
  8. data/README.md +42 -43
  9. data/Rakefile +13 -23
  10. data/lib/rom.rb +19 -27
  11. data/lib/rom/command.rb +118 -0
  12. data/lib/rom/command_registry.rb +13 -27
  13. data/lib/rom/commands.rb +1 -59
  14. data/lib/rom/commands/abstract.rb +147 -0
  15. data/lib/rom/commands/composite.rb +47 -0
  16. data/lib/rom/commands/create.rb +2 -17
  17. data/lib/rom/commands/delete.rb +5 -25
  18. data/lib/rom/commands/result.rb +5 -5
  19. data/lib/rom/commands/update.rb +3 -27
  20. data/lib/rom/constants.rb +19 -0
  21. data/lib/rom/env.rb +85 -35
  22. data/lib/rom/global.rb +173 -42
  23. data/lib/rom/header.rb +5 -5
  24. data/lib/rom/header/attribute.rb +2 -2
  25. data/lib/rom/lint/enumerable_dataset.rb +52 -0
  26. data/lib/rom/lint/linter.rb +64 -0
  27. data/lib/rom/lint/repository.rb +78 -0
  28. data/lib/rom/lint/spec.rb +20 -0
  29. data/lib/rom/lint/test.rb +98 -0
  30. data/lib/rom/mapper.rb +32 -5
  31. data/lib/rom/mapper/attribute_dsl.rb +240 -0
  32. data/lib/rom/mapper/dsl.rb +100 -0
  33. data/lib/rom/mapper/model_dsl.rb +55 -0
  34. data/lib/rom/mapper_registry.rb +8 -1
  35. data/lib/rom/memory.rb +4 -0
  36. data/lib/rom/memory/commands.rb +46 -0
  37. data/lib/rom/memory/dataset.rb +72 -0
  38. data/lib/rom/memory/relation.rb +44 -0
  39. data/lib/rom/memory/repository.rb +62 -0
  40. data/lib/rom/memory/storage.rb +57 -0
  41. data/lib/rom/model_builder.rb +44 -5
  42. data/lib/rom/processor.rb +1 -1
  43. data/lib/rom/processor/transproc.rb +109 -16
  44. data/lib/rom/reader.rb +91 -39
  45. data/lib/rom/relation.rb +165 -26
  46. data/lib/rom/relation/composite.rb +132 -0
  47. data/lib/rom/relation/curried.rb +48 -0
  48. data/lib/rom/relation/lazy.rb +173 -0
  49. data/lib/rom/relation/loaded.rb +75 -0
  50. data/lib/rom/relation/registry_reader.rb +23 -0
  51. data/lib/rom/repository.rb +93 -34
  52. data/lib/rom/setup.rb +54 -98
  53. data/lib/rom/setup/finalize.rb +85 -76
  54. data/lib/rom/setup_dsl/command.rb +36 -0
  55. data/lib/rom/setup_dsl/command_dsl.rb +34 -0
  56. data/lib/rom/setup_dsl/mapper.rb +32 -0
  57. data/lib/rom/setup_dsl/mapper_dsl.rb +30 -0
  58. data/lib/rom/setup_dsl/relation.rb +21 -0
  59. data/lib/rom/setup_dsl/setup.rb +75 -0
  60. data/lib/rom/support/array_dataset.rb +38 -0
  61. data/lib/rom/support/class_builder.rb +44 -0
  62. data/lib/rom/support/class_macros.rb +56 -0
  63. data/lib/rom/support/data_proxy.rb +102 -0
  64. data/lib/rom/support/enumerable_dataset.rb +58 -0
  65. data/lib/rom/support/inflector.rb +73 -0
  66. data/lib/rom/support/options.rb +188 -0
  67. data/lib/rom/support/registry.rb +4 -8
  68. data/lib/rom/version.rb +1 -1
  69. data/rakelib/benchmark.rake +13 -0
  70. data/rakelib/mutant.rake +16 -0
  71. data/rakelib/rubocop.rake +18 -0
  72. data/rom.gemspec +4 -7
  73. data/spec/integration/commands/create_spec.rb +32 -24
  74. data/spec/integration/commands/delete_spec.rb +15 -7
  75. data/spec/integration/commands/update_spec.rb +13 -11
  76. data/spec/integration/mappers/deep_embedded_spec.rb +4 -11
  77. data/spec/integration/mappers/definition_dsl_spec.rb +31 -44
  78. data/spec/integration/mappers/embedded_spec.rb +9 -24
  79. data/spec/integration/mappers/group_spec.rb +22 -30
  80. data/spec/integration/mappers/prefixing_attributes_spec.rb +18 -23
  81. data/spec/integration/mappers/renaming_attributes_spec.rb +23 -38
  82. data/spec/integration/mappers/symbolizing_attributes_spec.rb +18 -24
  83. data/spec/integration/mappers/wrap_spec.rb +22 -30
  84. data/spec/integration/multi_repo_spec.rb +15 -37
  85. data/spec/integration/relations/reading_spec.rb +82 -14
  86. data/spec/integration/repositories/extending_relations_spec.rb +50 -0
  87. data/spec/integration/{adapters → repositories}/setting_logger_spec.rb +6 -5
  88. data/spec/integration/setup_spec.rb +59 -62
  89. data/spec/shared/enumerable_dataset.rb +49 -0
  90. data/spec/shared/one_behavior.rb +26 -0
  91. data/spec/shared/users_and_tasks.rb +11 -23
  92. data/spec/spec_helper.rb +16 -7
  93. data/spec/support/constant_leak_finder.rb +14 -0
  94. data/spec/test/memory_repository_lint_test.rb +27 -0
  95. data/spec/unit/rom/command_registry_spec.rb +44 -0
  96. data/spec/unit/rom/commands/result_spec.rb +14 -0
  97. data/spec/unit/rom/commands_spec.rb +174 -0
  98. data/spec/unit/rom/env_spec.rb +40 -7
  99. data/spec/unit/rom/global_spec.rb +14 -0
  100. data/spec/unit/rom/{mapper_builder_spec.rb → mapper/dsl_spec.rb} +52 -38
  101. data/spec/unit/rom/mapper_spec.rb +51 -10
  102. data/spec/unit/rom/{adapter/memory → memory}/dataset_spec.rb +6 -4
  103. data/spec/unit/rom/memory/repository_spec.rb +12 -0
  104. data/spec/unit/rom/memory/storage_spec.rb +45 -0
  105. data/spec/unit/rom/model_builder_spec.rb +4 -3
  106. data/spec/unit/rom/processor/transproc_spec.rb +1 -0
  107. data/spec/unit/rom/reader_spec.rb +97 -24
  108. data/spec/unit/rom/relation/composite_spec.rb +65 -0
  109. data/spec/unit/rom/relation/lazy_spec.rb +145 -0
  110. data/spec/unit/rom/relation/loaded_spec.rb +28 -0
  111. data/spec/unit/rom/relation_spec.rb +111 -6
  112. data/spec/unit/rom/repository_spec.rb +59 -9
  113. data/spec/unit/rom/setup_spec.rb +99 -11
  114. data/spec/unit/rom/support/array_dataset_spec.rb +59 -0
  115. data/spec/unit/rom/support/class_builder_spec.rb +42 -0
  116. data/spec/unit/rom/support/enumerable_dataset_spec.rb +17 -0
  117. data/spec/unit/rom/support/inflector_spec.rb +89 -0
  118. data/spec/unit/rom/support/options_spec.rb +119 -0
  119. metadata +74 -112
  120. data/lib/rom/adapter.rb +0 -191
  121. data/lib/rom/adapter/memory.rb +0 -32
  122. data/lib/rom/adapter/memory/commands.rb +0 -31
  123. data/lib/rom/adapter/memory/dataset.rb +0 -67
  124. data/lib/rom/adapter/memory/storage.rb +0 -26
  125. data/lib/rom/commands/with_options.rb +0 -18
  126. data/lib/rom/config.rb +0 -70
  127. data/lib/rom/mapper_builder.rb +0 -52
  128. data/lib/rom/mapper_builder/mapper_dsl.rb +0 -114
  129. data/lib/rom/mapper_builder/model_dsl.rb +0 -29
  130. data/lib/rom/reader_builder.rb +0 -48
  131. data/lib/rom/relation_builder.rb +0 -62
  132. data/lib/rom/setup/base_relation_dsl.rb +0 -46
  133. data/lib/rom/setup/command_dsl.rb +0 -46
  134. data/lib/rom/setup/mapper_dsl.rb +0 -19
  135. data/lib/rom/setup/relation_dsl.rb +0 -20
  136. data/lib/rom/setup/schema_dsl.rb +0 -33
  137. data/spec/integration/adapters/extending_relations_spec.rb +0 -41
  138. data/spec/integration/commands/try_spec.rb +0 -27
  139. data/spec/integration/schema_spec.rb +0 -77
  140. data/spec/unit/config_spec.rb +0 -60
  141. data/spec/unit/rom/adapter_spec.rb +0 -79
  142. data/spec/unit/rom_spec.rb +0 -14
@@ -0,0 +1,75 @@
1
+ module ROM
2
+ class Relation
3
+ # Materializes a relation and exposes interface to access the data
4
+ #
5
+ # @api public
6
+ class Loaded
7
+ include Enumerable
8
+
9
+ # Source relation
10
+ #
11
+ # @return [Relation]
12
+ #
13
+ # @api private
14
+ attr_reader :source
15
+
16
+ # Materialized relation
17
+ #
18
+ # @return [Object]
19
+ #
20
+ # @api private
21
+ attr_reader :collection
22
+
23
+ # @api private
24
+ def initialize(source, collection = source.to_a)
25
+ @source = source
26
+ @collection = collection
27
+ end
28
+
29
+ # Yield relation tuples
30
+ #
31
+ # @yield [Hash]
32
+ #
33
+ # @api public
34
+ def each(&block)
35
+ return to_enum unless block
36
+ collection.each { |object| yield(object) }
37
+ end
38
+
39
+ # @api public
40
+ def new(collection)
41
+ self.class.new(source, collection)
42
+ end
43
+
44
+ # Returns a single tuple from the relation if there is one.
45
+ #
46
+ # @raise [ROM::TupleCountMismatchError] if the relation contains more than
47
+ # one tuple
48
+ #
49
+ # @api public
50
+ def one
51
+ if collection.count > 1
52
+ raise(
53
+ TupleCountMismatchError,
54
+ 'The relation consists of more than one tuple'
55
+ )
56
+ else
57
+ collection.first
58
+ end
59
+ end
60
+
61
+ # Like [one], but additionally raises an error if the relation is empty.
62
+ #
63
+ # @raise [ROM::TupleCountMismatchError] if the relation does not contain
64
+ # exactly one tuple
65
+ #
66
+ # @api public
67
+ def one!
68
+ one || raise(
69
+ TupleCountMismatchError,
70
+ 'The relation does not contain any tuples'
71
+ )
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,23 @@
1
+ module ROM
2
+ class Relation
3
+ module RegistryReader
4
+ # @api private
5
+ def self.included(klass)
6
+ super
7
+ klass.option :__registry__, type: Hash, default: {}, reader: true
8
+ end
9
+
10
+ # @api private
11
+ def respond_to_missing?(name, _include_private = false)
12
+ __registry__.key?(name) || super
13
+ end
14
+
15
+ private
16
+
17
+ # @api private
18
+ def method_missing(name, *)
19
+ __registry__.fetch(name) { super }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,66 +1,125 @@
1
1
  module ROM
2
- # Repository exposes native database connection and schema when it's
3
- # supported by the adapter
2
+ # Abstract repository class
4
3
  #
5
4
  # @api public
6
5
  class Repository
7
- include Concord::Public.new(:adapter)
6
+ # Return connection object
7
+ #
8
+ # @return [Object] type varies depending on the repository
9
+ #
10
+ # @api public
11
+ attr_reader :connection
8
12
 
9
- # Return the dataset identified by name
13
+ # Setup a repository
14
+ #
15
+ # @overload setup(type, *args)
16
+ # Sets up a single-repository given a repository type.
17
+ # For custom repositories, create an instance and pass it directly.
18
+ #
19
+ # @param [Symbol] type
20
+ # @param [Array] *args
21
+ #
22
+ # @overload setup(repository)
23
+ # @param [Repository] repository
24
+ #
25
+ # @return [Repository] a specific repository subclass
26
+ #
27
+ # @example
28
+ # module SuperDB
29
+ # class Repository < ROM::Repository
30
+ # def initialize(options)
31
+ # end
32
+ # end
33
+ # end
34
+ #
35
+ # ROM.register_adapter(:super_db, SuperDB)
10
36
  #
11
- # @param [String,Symbol] name
37
+ # Repository.setup(:super_db, some: 'options')
38
+ # # SuperDB::Repository.new(some: 'options') is called
39
+ #
40
+ # # or alternatively
41
+ # super_db = Repository.setup(SuperDB::Repository.new(some: 'options'))
42
+ # Repository.setup(super_db)
12
43
  #
13
44
  # @api public
14
- def [](name)
15
- adapter[name]
45
+ def self.setup(repository_or_scheme, *args)
46
+ case repository_or_scheme
47
+ when String
48
+ raise ArgumentError, <<-STRING.gsub(/^ {10}/, '')
49
+ URIs without an explicit scheme are not supported anymore.
50
+ See https://github.com/rom-rb/rom/blob/master/CHANGELOG.md
51
+ STRING
52
+ when Symbol
53
+ class_from_symbol(repository_or_scheme).new(*args)
54
+ else
55
+ if args.empty?
56
+ repository_or_scheme
57
+ else
58
+ raise ArgumentError, "Can't accept arguments when passing an instance"
59
+ end
60
+ end
16
61
  end
17
62
 
18
- # Set a logger for the adapter
63
+ # Get repository subclass for a specific adapter
19
64
  #
20
- # @param [Object] logger
65
+ # @param [Symbol] type adapter identifier
21
66
  #
22
- # @api public
23
- def use_logger(logger)
24
- adapter.logger = logger
67
+ # @return [Class]
68
+ #
69
+ # @api private
70
+ def self.class_from_symbol(type)
71
+ begin
72
+ require "rom/#{type}"
73
+ rescue LoadError
74
+ raise AdapterLoadError, "Failed to load adapter rom/#{type}"
75
+ end
76
+
77
+ adapter = ROM.adapters.fetch(type)
78
+ adapter.const_get(:Repository)
25
79
  end
26
80
 
27
- # Return logger used by the adapter
81
+ # A generic interface for setting up a logger
28
82
  #
29
- # @return [Object] logger
83
+ # @api public
84
+ def use_logger(*)
85
+ # noop
86
+ end
87
+
88
+ # A generic interface for returning default logger
30
89
  #
31
90
  # @api public
32
91
  def logger
33
- adapter.logger
92
+ # noop
34
93
  end
35
94
 
36
- # Return the database connection provided by the adapter
95
+ # Extension hook for adding repository-specific behavior to a command class
96
+ #
97
+ # @param [Class] klass command class
98
+ # @param [Object] _dataset dataset that will be used with this command class
99
+ #
100
+ # @return [Class]
37
101
  #
38
102
  # @api public
39
- def connection
40
- adapter.connection
103
+ def extend_command_class(klass, _dataset)
104
+ klass
41
105
  end
42
106
 
43
- # Return the schema provided by the adapter
107
+ # Schema inference hook
108
+ #
109
+ # Every repository that supports schema inference should implement this method
110
+ #
111
+ # @return [Array] array with datasets and their names
44
112
  #
45
113
  # @api private
46
114
  def schema
47
- adapter.schema
48
- end
49
-
50
- # @api private
51
- def respond_to_missing?(name, include_private = false)
52
- adapter.dataset?(name) || super
115
+ []
53
116
  end
54
117
 
55
- private
56
-
57
- # @api private
58
- def method_missing(name, *args, &block)
59
- if adapter.dataset?(name)
60
- adapter[name]
61
- else
62
- super
63
- end
118
+ # Disconnect is optional and it's a no-op by default
119
+ #
120
+ # @api public
121
+ def disconnect
122
+ # noop
64
123
  end
65
124
  end
66
125
  end
data/lib/rom/setup.rb CHANGED
@@ -1,128 +1,67 @@
1
- require 'rom/setup/schema_dsl'
2
- require 'rom/setup/mapper_dsl'
3
- require 'rom/setup/command_dsl'
4
-
5
1
  require 'rom/setup/finalize'
6
2
 
7
3
  module ROM
8
- # Exposes DSL for defining schema, relations, mappers and commands
4
+ # Exposes DSL for defining relations, mappers and commands
9
5
  #
10
- # @public
6
+ # @api public
11
7
  class Setup
12
8
  include Equalizer.new(:repositories, :env)
13
9
 
10
+ # @return [Hash] configured repositories
11
+ #
14
12
  # @api private
15
- attr_reader :repositories, :env
13
+ attr_reader :repositories
16
14
 
15
+ # @return [Symbol] default (first) adapter
16
+ #
17
17
  # @api private
18
- def initialize(repositories)
19
- @repositories = repositories
20
- @schema = {}
21
- @relations = {}
22
- @mappers = []
23
- @commands = {}
24
- @adapter_relation_map = {}
25
- @env = nil
26
- end
18
+ attr_reader :default_adapter
27
19
 
28
- # Schema definition DSL
20
+ # @return [Array] registered relation subclasses
29
21
  #
30
- # @example
31
- #
32
- # setup.schema do
33
- # base_relation(:users) do
34
- # repository :sqlite
35
- #
36
- # attribute :id
37
- # attribute :name
38
- # end
39
- # end
40
- #
41
- # @api public
42
- def schema(&block)
43
- SchemaDSL.new(self, @schema, &block)
44
- end
22
+ # @api private
23
+ attr_reader :relation_classes
45
24
 
46
- # Relation definition DSL
25
+ # @return [Array] registered mapper subclasses
47
26
  #
48
- # @example
49
- #
50
- # setup.relation(:users) do
51
- # def names
52
- # project(:name)
53
- # end
54
- # end
55
- #
56
- # @api public
57
- def relation(name, &block)
58
- @relations.update(name => block)
59
- end
27
+ # @api private
28
+ attr_reader :mapper_classes
60
29
 
61
- # Mapper definition DSL
62
- #
63
- # @example
30
+ # @return [Array] registered command subclasses
64
31
  #
65
- # setup.mappers do
66
- # define(:users) do
67
- # model name: 'User'
68
- # end
69
- #
70
- # define(:names, parent: :users) do
71
- # exclude :id
72
- # end
73
- # end
74
- #
75
- # @api public
76
- def mappers(&block)
77
- dsl = MapperDSL.new(&block)
78
- @mappers.concat(dsl.mappers)
79
- end
32
+ # @api private
33
+ attr_reader :command_classes
80
34
 
81
- # Command definition DSL
82
- #
83
- # @example
35
+ # @return [Env] finalized env after setup phase is over
84
36
  #
85
- # setup.commands(:users) do
86
- # define(:create) do
87
- # input NewUserParams
88
- # validator NewUserValidator
89
- # result :one
90
- # end
91
- #
92
- # define(:update) do
93
- # input UserParams
94
- # validator UserValidator
95
- # result :many
96
- # end
97
- #
98
- # define(:delete) do
99
- # result :many
100
- # end
101
- # end
102
- #
103
- # @api public
104
- def commands(name, &block)
105
- dsl = CommandDSL.new(&block)
106
- @commands.update(name => dsl.commands)
37
+ # @api private
38
+ attr_reader :env
39
+
40
+ # @api private
41
+ def initialize(repositories, default_adapter = nil)
42
+ @repositories = repositories
43
+ @default_adapter = default_adapter
44
+ @relation_classes = []
45
+ @mapper_classes = []
46
+ @command_classes = []
47
+ @env = nil
107
48
  end
108
49
 
109
50
  # Finalize the setup
110
51
  #
111
- # @return [Env] frozen env with access to repositories, schema, relations,
52
+ # @return [Env] frozen env with access to repositories, relations,
112
53
  # mappers and commands
113
54
  #
114
55
  # @api public
115
56
  def finalize
116
57
  raise EnvAlreadyFinalizedError if env
117
-
118
58
  finalize = Finalize.new(
119
- repositories, @schema, @relations, @mappers, @commands
59
+ repositories, relation_classes, mapper_classes, command_classes
120
60
  )
121
-
122
61
  @env = finalize.run!
123
62
  end
124
63
 
125
- # Returns repository identified by name
64
+ # Return repository identified by name
126
65
  #
127
66
  # @return [Repository]
128
67
  #
@@ -131,6 +70,27 @@ module ROM
131
70
  repositories.fetch(name)
132
71
  end
133
72
 
73
+ # Relation sub-classes are being registered with this method during setup
74
+ #
75
+ # @api private
76
+ def register_relation(klass)
77
+ @relation_classes << klass
78
+ end
79
+
80
+ # Mapper sub-classes are being registered with this method during setup
81
+ #
82
+ # @api private
83
+ def register_mapper(klass)
84
+ @mapper_classes << klass
85
+ end
86
+
87
+ # Command sub-classes are being registered with this method during setup
88
+ #
89
+ # @api private
90
+ def register_command(klass)
91
+ @command_classes << klass
92
+ end
93
+
134
94
  # Hook for respond_to? used internally
135
95
  #
136
96
  # @api private
@@ -146,11 +106,7 @@ module ROM
146
106
  #
147
107
  # @api private
148
108
  def method_missing(name, *)
149
- if repositories.key?(name)
150
- repositories.fetch(name)
151
- else
152
- super
153
- end
109
+ repositories.fetch(name) { super }
154
110
  end
155
111
  end
156
112
  end