rom 0.4.2 → 0.5.0

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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +81 -0
  3. data/.travis.yml +2 -1
  4. data/CHANGELOG.md +41 -0
  5. data/Gemfile +12 -8
  6. data/Guardfile +17 -11
  7. data/README.md +7 -7
  8. data/Rakefile +29 -0
  9. data/lib/rom.rb +9 -66
  10. data/lib/rom/adapter.rb +45 -12
  11. data/lib/rom/adapter/memory.rb +0 -4
  12. data/lib/rom/adapter/memory/commands.rb +0 -10
  13. data/lib/rom/adapter/memory/dataset.rb +18 -6
  14. data/lib/rom/adapter/memory/storage.rb +0 -3
  15. data/lib/rom/command_registry.rb +24 -43
  16. data/lib/rom/commands.rb +5 -6
  17. data/lib/rom/commands/create.rb +5 -5
  18. data/lib/rom/commands/delete.rb +8 -6
  19. data/lib/rom/commands/result.rb +82 -0
  20. data/lib/rom/commands/update.rb +5 -4
  21. data/lib/rom/commands/with_options.rb +1 -4
  22. data/lib/rom/config.rb +70 -0
  23. data/lib/rom/env.rb +11 -3
  24. data/lib/rom/global.rb +107 -0
  25. data/lib/rom/header.rb +122 -89
  26. data/lib/rom/header/attribute.rb +148 -0
  27. data/lib/rom/mapper.rb +46 -67
  28. data/lib/rom/mapper_builder.rb +20 -73
  29. data/lib/rom/mapper_builder/mapper_dsl.rb +114 -0
  30. data/lib/rom/mapper_builder/model_dsl.rb +29 -0
  31. data/lib/rom/mapper_registry.rb +21 -0
  32. data/lib/rom/model_builder.rb +11 -17
  33. data/lib/rom/processor.rb +28 -0
  34. data/lib/rom/processor/transproc.rb +105 -0
  35. data/lib/rom/reader.rb +81 -21
  36. data/lib/rom/reader_builder.rb +14 -4
  37. data/lib/rom/relation.rb +19 -5
  38. data/lib/rom/relation_builder.rb +20 -6
  39. data/lib/rom/repository.rb +0 -2
  40. data/lib/rom/setup.rb +156 -0
  41. data/lib/rom/{boot → setup}/base_relation_dsl.rb +4 -8
  42. data/lib/rom/setup/command_dsl.rb +46 -0
  43. data/lib/rom/setup/finalize.rb +125 -0
  44. data/lib/rom/setup/mapper_dsl.rb +19 -0
  45. data/lib/rom/{boot → setup}/relation_dsl.rb +1 -4
  46. data/lib/rom/setup/schema_dsl.rb +33 -0
  47. data/lib/rom/support/registry.rb +10 -6
  48. data/lib/rom/version.rb +1 -1
  49. data/rom.gemspec +3 -1
  50. data/spec/integration/adapters/extending_relations_spec.rb +0 -2
  51. data/spec/integration/commands/create_spec.rb +2 -9
  52. data/spec/integration/commands/delete_spec.rb +4 -5
  53. data/spec/integration/commands/error_handling_spec.rb +4 -3
  54. data/spec/integration/commands/update_spec.rb +3 -8
  55. data/spec/integration/mappers/deep_embedded_spec.rb +52 -0
  56. data/spec/integration/mappers/definition_dsl_spec.rb +0 -118
  57. data/spec/integration/mappers/embedded_spec.rb +82 -0
  58. data/spec/integration/mappers/group_spec.rb +170 -0
  59. data/spec/integration/mappers/prefixing_attributes_spec.rb +2 -2
  60. data/spec/integration/mappers/renaming_attributes_spec.rb +8 -6
  61. data/spec/integration/mappers/symbolizing_attributes_spec.rb +80 -0
  62. data/spec/integration/mappers/wrap_spec.rb +162 -0
  63. data/spec/integration/multi_repo_spec.rb +64 -0
  64. data/spec/integration/relations/reading_spec.rb +12 -8
  65. data/spec/integration/relations/registry_dsl_spec.rb +1 -3
  66. data/spec/integration/schema_spec.rb +10 -0
  67. data/spec/integration/setup_spec.rb +57 -6
  68. data/spec/spec_helper.rb +2 -1
  69. data/spec/unit/config_spec.rb +60 -0
  70. data/spec/unit/rom/adapter/memory/dataset_spec.rb +52 -0
  71. data/spec/unit/rom/adapter_spec.rb +31 -11
  72. data/spec/unit/rom/header_spec.rb +60 -16
  73. data/spec/unit/rom/mapper_builder_spec.rb +311 -0
  74. data/spec/unit/rom/mapper_registry_spec.rb +25 -0
  75. data/spec/unit/rom/mapper_spec.rb +4 -5
  76. data/spec/unit/rom/model_builder_spec.rb +15 -13
  77. data/spec/unit/rom/processor/transproc_spec.rb +331 -0
  78. data/spec/unit/rom/reader_spec.rb +73 -0
  79. data/spec/unit/rom/registry_spec.rb +38 -0
  80. data/spec/unit/rom/relation_spec.rb +0 -1
  81. data/spec/unit/rom/setup_spec.rb +55 -0
  82. data/spec/unit/rom_spec.rb +14 -0
  83. metadata +62 -22
  84. data/Gemfile.devtools +0 -71
  85. data/lib/rom/boot.rb +0 -197
  86. data/lib/rom/boot/command_dsl.rb +0 -48
  87. data/lib/rom/boot/dsl.rb +0 -37
  88. data/lib/rom/boot/mapper_dsl.rb +0 -23
  89. data/lib/rom/boot/schema_dsl.rb +0 -27
  90. data/lib/rom/ra.rb +0 -172
  91. data/lib/rom/ra/operation/group.rb +0 -47
  92. data/lib/rom/ra/operation/join.rb +0 -39
  93. data/lib/rom/ra/operation/wrap.rb +0 -45
  94. data/lib/rom/transformer.rb +0 -77
  95. data/spec/integration/ra/group_spec.rb +0 -46
  96. data/spec/integration/ra/join_spec.rb +0 -50
  97. data/spec/integration/ra/wrap_spec.rb +0 -37
  98. data/spec/unit/rom/ra/operation/group_spec.rb +0 -55
  99. data/spec/unit/rom/ra/operation/wrap_spec.rb +0 -29
  100. data/spec/unit/rom/transformer_spec.rb +0 -41
@@ -4,7 +4,6 @@ require 'rom/adapter/memory/commands'
4
4
 
5
5
  module ROM
6
6
  class Adapter
7
-
8
7
  class Memory < Adapter
9
8
  attr_accessor :logger
10
9
 
@@ -28,9 +27,6 @@ module ROM
28
27
  def [](name)
29
28
  connection[name]
30
29
  end
31
-
32
- Adapter.register(self)
33
30
  end
34
-
35
31
  end
36
32
  end
@@ -1,41 +1,31 @@
1
1
  module ROM
2
2
  class Adapter
3
3
  class Memory < Adapter
4
-
5
4
  module Commands
6
-
7
5
  class Create < ROM::Commands::Create
8
-
9
6
  def execute(tuple)
10
7
  attributes = input[tuple]
11
8
  validator.call(attributes)
12
9
  [relation.insert(attributes.to_h).to_a.last]
13
10
  end
14
-
15
11
  end
16
12
 
17
13
  class Update < ROM::Commands::Update
18
-
19
14
  def execute(params)
20
15
  attributes = input[params]
21
16
  validator.call(attributes)
22
17
  relation.map { |tuple| tuple.update(attributes.to_h) }
23
18
  end
24
-
25
19
  end
26
20
 
27
21
  class Delete < ROM::Commands::Delete
28
-
29
22
  def execute
30
23
  tuples = target.to_a
31
24
  tuples.each { |tuple| relation.delete(tuple) }
32
25
  tuples
33
26
  end
34
-
35
27
  end
36
-
37
28
  end
38
-
39
29
  end
40
30
  end
41
31
  end
@@ -1,7 +1,6 @@
1
1
  module ROM
2
2
  class Adapter
3
3
  class Memory < Adapter
4
-
5
4
  class Dataset
6
5
  include Charlatan.new(:data)
7
6
 
@@ -22,16 +21,31 @@ module ROM
22
21
  data.each(&block)
23
22
  end
24
23
 
25
- def restrict(criteria = nil, &block)
24
+ def join(*args)
25
+ left, right = args.size > 1 ? args : [self, args.first]
26
+
27
+ join_map = left.to_a.each_with_object({}) { |tuple, h|
28
+ others = right.to_a.find_all { |t| (tuple.to_a & t.to_a).any? }
29
+ (h[tuple] ||= []).concat(others)
30
+ }
31
+
32
+ tuples = left.map { |tuple|
33
+ join_map[tuple].map { |other| tuple.merge(other) }
34
+ }.flatten
35
+
36
+ self.class.new(tuples, left.header + right.header)
37
+ end
38
+
39
+ def restrict(criteria = nil)
26
40
  if criteria
27
- find_all { |tuple| criteria.all? { |k, v| tuple[k] == v } }
41
+ find_all { |tuple| criteria.all? { |k, v| tuple[k].eql?(v) } }
28
42
  else
29
43
  find_all { |tuple| yield(tuple) }
30
44
  end
31
45
  end
32
46
 
33
47
  def project(*names)
34
- map { |tuple| tuple.reject { |key,_| names.include?(key) } }
48
+ map { |tuple| tuple.reject { |key| !names.include?(key) } }
35
49
  end
36
50
 
37
51
  def order(*names)
@@ -47,9 +61,7 @@ module ROM
47
61
  data.delete(tuple)
48
62
  self
49
63
  end
50
-
51
64
  end
52
-
53
65
  end
54
66
  end
55
67
  end
@@ -1,7 +1,6 @@
1
1
  module ROM
2
2
  class Adapter
3
3
  class Memory < Adapter
4
-
5
4
  class Storage
6
5
  attr_reader :data
7
6
 
@@ -21,9 +20,7 @@ module ROM
21
20
  def key?(name)
22
21
  data.key?(name)
23
22
  end
24
-
25
23
  end
26
-
27
24
  end
28
25
  end
29
26
  end
@@ -1,50 +1,21 @@
1
- module ROM
2
-
3
- class Result
4
- attr_reader :value, :error
5
-
6
- def to_ary
7
- raise NotImplementedError
8
- end
9
- alias_method :to_a, :to_ary
10
-
11
- class Success < Result
12
- def initialize(value)
13
- @value = value
14
- end
15
-
16
- def >(f)
17
- f.call(value)
18
- end
19
-
20
- def to_ary
21
- value
22
- end
23
- alias_method :to_a, :to_ary
24
- end
25
-
26
- class Failure < Result
27
- def initialize(error)
28
- @error = error
29
- end
30
-
31
- def >(f)
32
- self
33
- end
34
-
35
- def to_ary
36
- error
37
- end
38
- end
39
- end
1
+ require 'rom/commands/result'
40
2
 
3
+ module ROM
4
+ # Command registry exposes "try" interface for executing commands
5
+ #
6
+ # @public
41
7
  class CommandRegistry < Registry
42
-
43
8
  class Evaluator
44
9
  include Concord.new(:registry)
45
10
 
46
11
  private
47
12
 
13
+ # Call a command when method is matching command name
14
+ #
15
+ # TODO: this will be replaced by explicit definition of methods for all
16
+ # registered commands
17
+ #
18
+ # @api public
48
19
  def method_missing(name, *args, &block)
49
20
  command = registry[name]
50
21
 
@@ -58,11 +29,21 @@ module ROM
58
29
  end
59
30
  end
60
31
 
32
+ # Try to execute a command in a block
33
+ #
34
+ # @example
35
+ #
36
+ # rom.command(:users).try { create(name: 'Jane') }
37
+ # rom.command(:users).try { update(:by_id, 1).set(name: 'Jane Doe') }
38
+ # rom.command(:users).try { delete(:by_id, 1) }
39
+ #
40
+ # @return [Commands::Result]
41
+ #
42
+ # @api public
61
43
  def try(&f)
62
- Result::Success.new(Evaluator.new(self).instance_exec(&f))
44
+ Commands::Result::Success.new(Evaluator.new(self).instance_exec(&f))
63
45
  rescue CommandError => e
64
- Result::Failure.new(e)
46
+ Commands::Result::Failure.new(e)
65
47
  end
66
48
  end
67
-
68
49
  end
@@ -1,6 +1,5 @@
1
1
  module ROM
2
2
  module Commands
3
-
4
3
  class AbstractCommand
5
4
  VALID_RESULTS = [:one, :many].freeze
6
5
 
@@ -13,7 +12,7 @@ module ROM
13
12
 
14
13
  @result = options[:result] || :many
15
14
 
16
- if !VALID_RESULTS.include?(result)
15
+ unless VALID_RESULTS.include?(result)
17
16
  raise InvalidOptionError.new(:result, VALID_RESULTS)
18
17
  end
19
18
  end
@@ -34,7 +33,8 @@ module ROM
34
33
  # Target relation on which the command will operate
35
34
  #
36
35
  # By default this is set to the relation that's passed to the constructor.
37
- # Specialized commands like Delete may set the target to a different relation.
36
+ # Specialized commands like Delete may set the target to a different
37
+ # relation.
38
38
  #
39
39
  # @return [Relation]
40
40
  #
@@ -43,7 +43,8 @@ module ROM
43
43
  relation
44
44
  end
45
45
 
46
- # Assert that tuple count in the target relation corresponds to :result setting
46
+ # Assert that tuple count in the target relation corresponds to :result
47
+ # setting
47
48
  #
48
49
  # @raises TupleCountMismatchError
49
50
  #
@@ -53,9 +54,7 @@ module ROM
53
54
  raise TupleCountMismatchError, "#{inspect} expects one tuple"
54
55
  end
55
56
  end
56
-
57
57
  end
58
-
59
58
  end
60
59
  end
61
60
 
@@ -2,7 +2,6 @@ require 'rom/commands/with_options'
2
2
 
3
3
  module ROM
4
4
  module Commands
5
-
6
5
  # Create command
7
6
  #
8
7
  # This command inserts a new tuple into a relation
@@ -18,11 +17,12 @@ module ROM
18
17
  # @return [Array] an array with inserted tuples
19
18
  #
20
19
  # @api private
21
- def execute(tuple)
22
- raise NotImplementedError, "#{self.class}##{__method__} must be implemented"
20
+ def execute(_tuple)
21
+ raise(
22
+ NotImplementedError,
23
+ "#{self.class}##{__method__} must be implemented"
24
+ )
23
25
  end
24
-
25
26
  end
26
-
27
27
  end
28
28
  end
@@ -1,6 +1,5 @@
1
1
  module ROM
2
2
  module Commands
3
-
4
3
  # Delete command
5
4
  #
6
5
  # This command removes tuples from its target relation
@@ -28,17 +27,20 @@ module ROM
28
27
  #
29
28
  # @api private
30
29
  def execute
31
- raise NotImplementedError, "#{self.class}##{__method__} must be implemented"
30
+ raise(
31
+ NotImplementedError,
32
+ "#{self.class}##{__method__} must be implemented"
33
+ )
32
34
  end
33
35
 
34
- # Return new delete command with new target
36
+ # Create a new delete command scoped to specific relation and execute it
35
37
  #
36
38
  # @api private
37
39
  def new(*args, &block)
38
- self.class.new(relation, options.merge(target: relation.public_send(*args, &block)))
40
+ new_options = options.merge(target: relation.public_send(*args, &block))
41
+ command = self.class.new(relation, new_options)
42
+ command.call
39
43
  end
40
-
41
44
  end
42
-
43
45
  end
44
46
  end
@@ -0,0 +1,82 @@
1
+ module ROM
2
+ module Commands
3
+ # Abstract result class for success and error results
4
+ #
5
+ # @public
6
+ class Result
7
+ # Return command execution result
8
+ #
9
+ # @api public
10
+ attr_reader :value
11
+
12
+ # Return potential command execution result error
13
+ #
14
+ # @api public
15
+ attr_reader :error
16
+
17
+ # Coerce result to an array
18
+ #
19
+ # @abstract
20
+ #
21
+ # @api public
22
+ def to_ary
23
+ raise NotImplementedError
24
+ end
25
+ alias_method :to_a, :to_ary
26
+
27
+ # Success result has a value and no error
28
+ #
29
+ # @public
30
+ class Success < Result
31
+ # @api private
32
+ def initialize(value)
33
+ @value = value
34
+ end
35
+
36
+ # Call next command on continuation
37
+ #
38
+ # @api public
39
+ def >(other)
40
+ other.call(value)
41
+ end
42
+
43
+ # Return the value
44
+ #
45
+ # @return [Array]
46
+ #
47
+ # @api public
48
+ def to_ary
49
+ value
50
+ end
51
+ end
52
+
53
+ # Failure result has an error and no value
54
+ #
55
+ # @public
56
+ class Failure < Result
57
+ # @api private
58
+ def initialize(error)
59
+ @error = error
60
+ end
61
+
62
+ # Do not call next command on continuation
63
+ #
64
+ # @return [self]
65
+ #
66
+ # @api public
67
+ def >(_other)
68
+ self
69
+ end
70
+
71
+ # Return the error
72
+ #
73
+ # @return [Array<CommandError>]
74
+ #
75
+ # @api public
76
+ def to_ary
77
+ error
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -2,7 +2,6 @@ require 'rom/commands/with_options'
2
2
 
3
3
  module ROM
4
4
  module Commands
5
-
6
5
  # Update command
7
6
  #
8
7
  # This command updates all tuples in its relation with new attributes
@@ -26,8 +25,11 @@ module ROM
26
25
  # @abstract
27
26
  #
28
27
  # @api private
29
- def execute(params)
30
- raise NotImplementedError, "#{self.class}##{__method__} must be implemented"
28
+ def execute(_params)
29
+ raise(
30
+ NotImplementedError,
31
+ "#{self.class}##{__method__} must be implemented"
32
+ )
31
33
  end
32
34
 
33
35
  # Return new update command with new relation
@@ -37,6 +39,5 @@ module ROM
37
39
  self.class.new(relation.public_send(*args, &block), options)
38
40
  end
39
41
  end
40
-
41
42
  end
42
43
  end
@@ -1,6 +1,5 @@
1
1
  module ROM
2
2
  module Commands
3
-
4
3
  # Common behavior for Create and Update commands
5
4
  #
6
5
  # TODO: find a better name for this module
@@ -11,11 +10,9 @@ module ROM
11
10
  def initialize(relation, options)
12
11
  super
13
12
 
14
- @validator = options[:validator] || Proc.new {}
13
+ @validator = options[:validator] || proc {}
15
14
  @input = options[:input] || Hash
16
15
  end
17
-
18
16
  end
19
-
20
17
  end
21
18
  end