rom 0.5.0 → 0.6.0.beta1

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.
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,100 @@
1
+ require 'rom/mapper/attribute_dsl'
2
+
3
+ module ROM
4
+ class Mapper
5
+ # Mapper class-level DSL including Attribute DSL and Model DSL
6
+ module DSL
7
+ # Extend mapper class with macros and DSL methods
8
+ #
9
+ # @api private
10
+ def self.included(klass)
11
+ klass.extend(ClassMacros)
12
+ klass.extend(ClassMethods)
13
+ end
14
+
15
+ # Class methods for all mappers
16
+ #
17
+ # @private
18
+ module ClassMethods
19
+ # Set base ivars for the mapper class
20
+ #
21
+ # @api private
22
+ def inherited(klass)
23
+ super
24
+
25
+ klass.instance_variable_set('@attributes', nil)
26
+ klass.instance_variable_set('@header', nil)
27
+ klass.instance_variable_set('@dsl', nil)
28
+ end
29
+
30
+ # Return base_relation used for creating mapper registry
31
+ #
32
+ # This is used to "gather" mappers under same root name
33
+ #
34
+ # @api private
35
+ def base_relation
36
+ if superclass.relation
37
+ superclass.relation
38
+ else
39
+ relation
40
+ end
41
+ end
42
+
43
+ # Return header of the mapper
44
+ #
45
+ # This is memoized so mutating mapper class won't have an effect wrt
46
+ # header after it was initialized for the first time.
47
+ #
48
+ # TODO: freezing mapper class here is probably a good idea
49
+ #
50
+ # @api private
51
+ def header
52
+ @header ||= dsl.header
53
+ end
54
+
55
+ private
56
+
57
+ # Return default Attribute DSL options based on settings of the mapper
58
+ # class
59
+ #
60
+ # @api private
61
+ def options
62
+ { prefix: prefix,
63
+ prefix_separator: prefix_separator,
64
+ symbolize_keys: symbolize_keys }
65
+ end
66
+
67
+ # Return default attributes that might have been inherited from the
68
+ # superclass
69
+ #
70
+ # @api private
71
+ def attributes
72
+ @attributes ||=
73
+ if superclass.respond_to?(:attributes, true) && inherit_header
74
+ superclass.attributes.dup
75
+ else
76
+ []
77
+ end
78
+ end
79
+
80
+ # Create the attribute DSL instance used by the mapper class
81
+ #
82
+ # @api private
83
+ def dsl
84
+ @dsl ||= AttributeDSL.new(attributes, options)
85
+ end
86
+
87
+ # Delegate Attribute DSL method to the dsl instance
88
+ #
89
+ # @api private
90
+ def method_missing(name, *args, &block)
91
+ if dsl.respond_to?(name)
92
+ dsl.public_send(name, *args, &block)
93
+ else
94
+ super
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,55 @@
1
+ require 'rom/model_builder'
2
+
3
+ module ROM
4
+ class Mapper
5
+ # Model DSL allows setting a model class
6
+ #
7
+ # @private
8
+ module ModelDSL
9
+ attr_reader :attributes, :builder, :klass
10
+
11
+ DEFAULT_TYPE = :poro
12
+
13
+ # Set or generate a model
14
+ #
15
+ # @example
16
+ # class MyDefinition
17
+ # include ROM::Mapper::ModelDSL
18
+ #
19
+ # def initialize
20
+ # @attributes = [[:name], [:title]]
21
+ # end
22
+ # end
23
+ #
24
+ # definition = MyDefinition.new
25
+ #
26
+ # # just set a model constant
27
+ # definition.model(User)
28
+ #
29
+ # # generate model class for the attributes
30
+ # definition.model(name: 'User')
31
+ #
32
+ # @api public
33
+ def model(options = nil)
34
+ if options.is_a?(Class)
35
+ @klass = options
36
+ elsif options
37
+ type = options.fetch(:type) { DEFAULT_TYPE }
38
+ @builder = ModelBuilder[type].new(options)
39
+ end
40
+
41
+ build_class unless options
42
+ end
43
+
44
+ private
45
+
46
+ # Build a model class using a specialized builder
47
+ #
48
+ # @api private
49
+ def build_class
50
+ return klass if klass
51
+ return builder.call(attributes.map(&:first)) if builder
52
+ end
53
+ end
54
+ end
55
+ end
@@ -6,9 +6,16 @@ module ROM
6
6
  elements[name] = mapper
7
7
  end
8
8
 
9
+ # @api private
10
+ def [](name)
11
+ elements.fetch(name) { raise(MapperMissingError, name) }
12
+ end
13
+
9
14
  # @api private
10
15
  def by_path(path)
11
- elements[paths(path).detect { |name| elements.key?(name) }]
16
+ elements.fetch(paths(path).detect { |name| elements.key?(name) }) {
17
+ raise(MapperMissingError, path)
18
+ }
12
19
  end
13
20
 
14
21
  private
data/lib/rom/memory.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'rom/memory/relation'
2
+ require 'rom/memory/repository'
3
+
4
+ ROM.register_adapter(:memory, ROM::Memory)
@@ -0,0 +1,46 @@
1
+ require 'rom/commands'
2
+
3
+ module ROM
4
+ module Memory
5
+ # Memory adapter commands namespace
6
+ #
7
+ # @api public
8
+ module Commands
9
+ # In-memory create command
10
+ #
11
+ # @api public
12
+ class Create < ROM::Commands::Create
13
+ # @see ROM::Commands::Create#execute
14
+ def execute(tuple)
15
+ attributes = input[tuple]
16
+ validator.call(attributes)
17
+ [relation.insert(attributes.to_h).to_a.last]
18
+ end
19
+ end
20
+
21
+ # In-memory update command
22
+ #
23
+ # @api public
24
+ class Update < ROM::Commands::Update
25
+ # @see ROM::Commands::Update#execute
26
+ def execute(params)
27
+ attributes = input[params]
28
+ validator.call(attributes)
29
+ relation.map { |tuple| tuple.update(attributes.to_h) }
30
+ end
31
+ end
32
+
33
+ # In-memory delete command
34
+ #
35
+ # @api public
36
+ class Delete < ROM::Commands::Delete
37
+ # @see ROM::Commands::Delete#execute
38
+ def execute
39
+ tuples = target.to_a
40
+ tuples.each { |tuple| relation.delete(tuple) }
41
+ tuples
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,72 @@
1
+ require 'rom/support/array_dataset'
2
+
3
+ module ROM
4
+ module Memory
5
+ # In-memory dataset
6
+ #
7
+ # @api public
8
+ class Dataset
9
+ include ArrayDataset
10
+
11
+ # Join two datasets
12
+ #
13
+ # @api public
14
+ def join(*args)
15
+ left, right = args.size > 1 ? args : [self, args.first]
16
+
17
+ join_map = left.each_with_object({}) { |tuple, h|
18
+ others = right.to_a.find_all { |t| (tuple.to_a & t.to_a).any? }
19
+ (h[tuple] ||= []).concat(others)
20
+ }
21
+
22
+ tuples = left.flat_map { |tuple|
23
+ join_map[tuple].map { |other| tuple.merge(other) }
24
+ }
25
+
26
+ self.class.new(tuples, row_proc)
27
+ end
28
+
29
+ # Restrict a dataset
30
+ #
31
+ # @api public
32
+ def restrict(criteria = nil)
33
+ if criteria
34
+ find_all { |tuple| criteria.all? { |k, v| tuple[k].eql?(v) } }
35
+ else
36
+ find_all { |tuple| yield(tuple) }
37
+ end
38
+ end
39
+
40
+ # Project a dataset
41
+ #
42
+ # @api public
43
+ def project(*names)
44
+ map { |tuple| tuple.reject { |key| !names.include?(key) } }
45
+ end
46
+
47
+ # Sort a dataset
48
+ #
49
+ # @api public
50
+ def order(*names)
51
+ sort_by { |tuple| tuple.values_at(*names) }
52
+ end
53
+
54
+ # Insert tuple into a dataset
55
+ #
56
+ # @api public
57
+ def insert(tuple)
58
+ data << tuple
59
+ self
60
+ end
61
+ alias_method :<<, :insert
62
+
63
+ # Delete tuples from a dataset
64
+ #
65
+ # @api public
66
+ def delete(tuple)
67
+ data.delete(tuple)
68
+ self
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,44 @@
1
+ module ROM
2
+ module Memory
3
+ # Relation subclass for memory adapter
4
+ #
5
+ # @example
6
+ # class Users < ROM::Relation[:memory]
7
+ # end
8
+ #
9
+ # @api public
10
+ class Relation < ROM::Relation
11
+ include Enumerable
12
+
13
+ forward :join, :project, :restrict, :order
14
+
15
+ # Insert tuples into the relation
16
+ #
17
+ # @example
18
+ # users.insert(name: 'Jane')
19
+ #
20
+ # @return [Relation]
21
+ #
22
+ # @api public
23
+ def insert(*args)
24
+ dataset.insert(*args)
25
+ self
26
+ end
27
+ alias_method :<<, :insert
28
+
29
+ # Delete tuples from the relation
30
+ #
31
+ # @example
32
+ # users.insert(name: 'Jane')
33
+ # users.delete(name: 'Jane')
34
+ #
35
+ # @return [Relation]
36
+ #
37
+ # @api public
38
+ def delete(*args)
39
+ dataset.delete(*args)
40
+ self
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,62 @@
1
+ require 'rom/repository'
2
+ require 'rom/memory/storage'
3
+ require 'rom/memory/commands'
4
+
5
+ module ROM
6
+ module Memory
7
+ # In-memory repository interface
8
+ #
9
+ # @example
10
+ # repository = ROM::Memory::Repository.new
11
+ # repository.dataset(:users)
12
+ # repository[:users].insert(name: 'Jane')
13
+ #
14
+ # @api public
15
+ class Repository < ROM::Repository
16
+ # @return [Object] default logger
17
+ #
18
+ # @api public
19
+ attr_reader :logger
20
+
21
+ # @api private
22
+ def initialize
23
+ @connection = Storage.new
24
+ end
25
+
26
+ # Set default logger for the repository
27
+ #
28
+ # @param [Object] logger object
29
+ #
30
+ # @api public
31
+ def use_logger(logger)
32
+ @logger = logger
33
+ end
34
+
35
+ # Register a dataset in the repository
36
+ #
37
+ # If dataset already exists it will be returned
38
+ #
39
+ # @return [Dataset]
40
+ #
41
+ # @api public
42
+ def dataset(name)
43
+ self[name] || connection.create_dataset(name)
44
+ end
45
+
46
+ # @see ROM::Repository#dataset?
47
+ def dataset?(name)
48
+ connection.key?(name)
49
+ end
50
+
51
+ # Return dataset with the given name
52
+ #
53
+ # @param (see ROM::Repository#[])
54
+ # @return [Memory::Dataset]
55
+ #
56
+ # @api public
57
+ def [](name)
58
+ connection[name]
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,57 @@
1
+ require 'thread_safe'
2
+ require 'rom/memory/dataset'
3
+
4
+ module ROM
5
+ module Memory
6
+ # In-memory thread-safe data storage
7
+ #
8
+ # @private
9
+ class Storage
10
+ # Dataset registry
11
+ #
12
+ # @return [ThreadSafe::Hash]
13
+ #
14
+ # @api private
15
+ attr_reader :data
16
+
17
+ # @api private
18
+ def initialize
19
+ @data = ThreadSafe::Hash.new
20
+ end
21
+
22
+ # @return [Dataset]
23
+ #
24
+ # @api private
25
+ def [](name)
26
+ data[name]
27
+ end
28
+
29
+ # Register a new dataset
30
+ #
31
+ # @return [Dataset]
32
+ #
33
+ # @api private
34
+ def create_dataset(name)
35
+ data[name] = Dataset.new(ThreadSafe::Array.new)
36
+ end
37
+
38
+ # Check if there's dataset under specified key
39
+ #
40
+ # @return [Boolean]
41
+ #
42
+ # @api private
43
+ def key?(name)
44
+ data.key?(name)
45
+ end
46
+
47
+ # Return registered datasets count
48
+ #
49
+ # @return [Integer]
50
+ #
51
+ # @api private
52
+ def size
53
+ data.size
54
+ end
55
+ end
56
+ end
57
+ end