rom-mapper 0.1.1 → 0.2.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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -1
  4. data/.travis.yml +19 -13
  5. data/{Changelog.md → CHANGELOG.md} +6 -0
  6. data/Gemfile +23 -10
  7. data/README.md +17 -12
  8. data/Rakefile +12 -4
  9. data/lib/rom-mapper.rb +6 -15
  10. data/lib/rom/header.rb +195 -0
  11. data/lib/rom/header/attribute.rb +184 -0
  12. data/lib/rom/mapper.rb +63 -100
  13. data/lib/rom/mapper/attribute_dsl.rb +477 -0
  14. data/lib/rom/mapper/dsl.rb +120 -0
  15. data/lib/rom/mapper/model_dsl.rb +55 -0
  16. data/lib/rom/mapper/version.rb +3 -7
  17. data/lib/rom/model_builder.rb +99 -0
  18. data/lib/rom/processor.rb +28 -0
  19. data/lib/rom/processor/transproc.rb +388 -0
  20. data/rakelib/benchmark.rake +15 -0
  21. data/rakelib/mutant.rake +16 -0
  22. data/rakelib/rubocop.rake +18 -0
  23. data/rom-mapper.gemspec +7 -6
  24. data/spec/spec_helper.rb +32 -33
  25. data/spec/support/constant_leak_finder.rb +14 -0
  26. data/spec/support/mutant.rb +10 -0
  27. data/spec/unit/rom/mapper/dsl_spec.rb +467 -0
  28. data/spec/unit/rom/mapper_spec.rb +83 -0
  29. data/spec/unit/rom/processor/transproc_spec.rb +448 -0
  30. metadata +68 -89
  31. data/.ruby-version +0 -1
  32. data/Gemfile.devtools +0 -55
  33. data/config/devtools.yml +0 -2
  34. data/config/flay.yml +0 -3
  35. data/config/flog.yml +0 -2
  36. data/config/mutant.yml +0 -3
  37. data/config/reek.yml +0 -103
  38. data/config/rubocop.yml +0 -45
  39. data/lib/rom/mapper/attribute.rb +0 -31
  40. data/lib/rom/mapper/dumper.rb +0 -27
  41. data/lib/rom/mapper/loader.rb +0 -22
  42. data/lib/rom/mapper/loader/allocator.rb +0 -32
  43. data/lib/rom/mapper/loader/attribute_writer.rb +0 -23
  44. data/lib/rom/mapper/loader/object_builder.rb +0 -28
  45. data/spec/shared/unit/loader_call.rb +0 -13
  46. data/spec/shared/unit/loader_identity.rb +0 -13
  47. data/spec/shared/unit/mapper_context.rb +0 -13
  48. data/spec/unit/rom/mapper/call_spec.rb +0 -32
  49. data/spec/unit/rom/mapper/class_methods/build_spec.rb +0 -64
  50. data/spec/unit/rom/mapper/dump_spec.rb +0 -15
  51. data/spec/unit/rom/mapper/dumper/call_spec.rb +0 -29
  52. data/spec/unit/rom/mapper/dumper/identity_spec.rb +0 -28
  53. data/spec/unit/rom/mapper/header/each_spec.rb +0 -28
  54. data/spec/unit/rom/mapper/header/element_reader_spec.rb +0 -25
  55. data/spec/unit/rom/mapper/header/keys_spec.rb +0 -32
  56. data/spec/unit/rom/mapper/identity_from_tuple_spec.rb +0 -15
  57. data/spec/unit/rom/mapper/identity_spec.rb +0 -15
  58. data/spec/unit/rom/mapper/load_spec.rb +0 -15
  59. data/spec/unit/rom/mapper/loader/allocator/call_spec.rb +0 -7
  60. data/spec/unit/rom/mapper/loader/allocator/identity_spec.rb +0 -7
  61. data/spec/unit/rom/mapper/loader/attribute_writer/call_spec.rb +0 -7
  62. data/spec/unit/rom/mapper/loader/attribute_writer/identity_spec.rb +0 -7
  63. data/spec/unit/rom/mapper/loader/object_builder/call_spec.rb +0 -7
  64. data/spec/unit/rom/mapper/loader/object_builder/identity_spec.rb +0 -7
  65. data/spec/unit/rom/mapper/model_spec.rb +0 -11
  66. data/spec/unit/rom/mapper/new_object_spec.rb +0 -14
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1305458b68360474e3be88eb5418ce057e7a5dc3
4
+ data.tar.gz: 69e816fabf9130eaf6daf3300f56b9efce63161f
5
+ SHA512:
6
+ metadata.gz: 1184db0c8ce2fd3bea1d7bb7cdec72a8f1a7f298f0be35482a2357081c9841896aeea87e0a648f5deab1af1cad8a5160ac888ebc9840cadf960952421143e4f5
7
+ data.tar.gz: fa2c178cdcf7ce602c4390a633fd255c2a04f15b5fe3425d90445ca6a6fa30bcb5ed1ed320adedee55e581e7f0c30530cbe7deb8fd618ff426f1947c866bfa4b
data/.gitignore CHANGED
@@ -2,6 +2,7 @@ log
2
2
  *.rbx
3
3
  *.rbc
4
4
  Gemfile.lock
5
+ vendor/
5
6
  tmp/
6
7
  doc/
7
8
  coverage/
@@ -15,3 +16,4 @@ Vagrantfile
15
16
 
16
17
  # spiking area
17
18
  scratchpad.rb
19
+ .ruby-version
data/.rspec CHANGED
@@ -1,3 +1,3 @@
1
1
  --color
2
- --profile
3
2
  --order random
3
+ --warnings
data/.travis.yml CHANGED
@@ -1,21 +1,27 @@
1
1
  language: ruby
2
- bundler_args: --without yard guard benchmarks
2
+ sudo: false
3
+ cache: bundler
4
+ bundler_args: --without sql benchmarks console tools
3
5
  script: "bundle exec rake ci"
4
6
  rvm:
5
- - 1.9.3
6
- - 2.0.0
7
+ - 2.0
8
+ - 2.1
9
+ - 2.2
10
+ - rbx-2
11
+ - jruby
7
12
  - ruby-head
8
- - rbx-19mode
13
+ - jruby-head
14
+ env:
15
+ global:
16
+ - JRUBY_OPTS='--dev -J-Xmx1024M'
9
17
  matrix:
10
18
  allow_failures:
11
- - rvm: jruby-19mode
12
19
  - rvm: ruby-head
20
+ - rvm: jruby-head
13
21
  notifications:
14
- irc:
15
- channels:
16
- - "irc.freenode.org#rom-rb"
17
- on_success: never
18
- on_failure: change
19
- email:
20
- on_success: never
21
- on_failure: change
22
+ webhooks:
23
+ urls:
24
+ - https://webhooks.gitter.im/e/39e1225f489f38b0bd09
25
+ on_success: change
26
+ on_failure: always
27
+ on_start: false
@@ -1,3 +1,9 @@
1
+ # v0.2.0 2015-08-10
2
+
3
+ Import code from rom 0.8.1
4
+
5
+ [Compare v0.1.1..v0.2.0](https://github.com/rom-rb/rom-mapper/compare/v0.1.0...v0.2.0)
6
+
1
7
  # v0.1.1 2013-09-02
2
8
 
3
9
  * [internal] Moved version file to rom/mapper/version (solnic)
data/Gemfile CHANGED
@@ -1,19 +1,32 @@
1
- # encoding: utf-8
2
-
3
1
  source 'https://rubygems.org'
4
2
 
5
3
  gemspec
6
4
 
7
- gem 'rom-mapper', path: '.'
8
-
9
5
  group :test do
10
- gem 'bogus', '~> 0.1'
11
- gem 'axiom', '~> 0.1'
6
+ gem 'anima'
7
+ gem 'virtus'
8
+ gem 'inflecto', '~> 0.0', '>= 0.0.2'
9
+
10
+ platforms :rbx do
11
+ gem 'codeclimate-test-reporter', require: false
12
+ end
12
13
  end
13
14
 
14
- group :development do
15
- gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
15
+ group :benchmarks do
16
+ gem 'benchmark-ips', '~> 2.2.0'
16
17
  end
17
18
 
18
- # Added by devtools
19
- eval_gemfile 'Gemfile.devtools'
19
+ group :tools do
20
+ gem 'rubocop', '~> 0.31'
21
+
22
+ gem 'guard'
23
+ gem 'guard-rspec'
24
+ gem 'guard-rubocop'
25
+
26
+ gem 'byebug'
27
+
28
+ platform :mri do
29
+ gem 'mutant', '>= 0.8.0', github: 'mbj/mutant', branch: 'master'
30
+ gem 'mutant-rspec'
31
+ end
32
+ end
data/README.md CHANGED
@@ -1,21 +1,26 @@
1
- # rom-mapper
2
-
3
- [![Gem Version](https://badge.fury.io/rb/rom-mapper.png)][gem]
4
- [![Build Status](https://travis-ci.org/rom-rb/rom-mapper.png?branch=master)][travis]
5
- [![Dependency Status](https://gemnasium.com/rom-rb/rom-mapper.png)][gemnasium]
6
- [![Code Climate](https://codeclimate.com/github/rom-rb/rom-mapper.png)][codeclimate]
7
- [![Coverage Status](https://coveralls.io/repos/rom-rb/rom-mapper/badge.png?branch=master)][coveralls]
8
-
9
1
  [gem]: https://rubygems.org/gems/rom-mapper
10
2
  [travis]: https://travis-ci.org/rom-rb/rom-mapper
11
3
  [gemnasium]: https://gemnasium.com/rom-rb/rom-mapper
12
4
  [codeclimate]: https://codeclimate.com/github/rom-rb/rom-mapper
13
- [coveralls]: https://coveralls.io/r/rom-rb/rom-mapper
5
+ [inchpages]: http://inch-ci.org/github/rom-rb/rom-mapper
6
+
7
+ # ROM::Mapper
8
+
9
+ [![Gem Version](https://badge.fury.io/rb/rom-mapper.svg)][gem]
10
+ [![Build Status](https://travis-ci.org/rom-rb/rom-mapper.svg?branch=master)][travis]
11
+ [![Dependency Status](https://gemnasium.com/rom-rb/rom-mapper.png)][gemnasium]
12
+ [![Code Climate](https://codeclimate.com/github/rom-rb/rom-mapper/badges/gpa.svg)][codeclimate]
13
+ [![Test Coverage](https://codeclimate.com/github/rom-rb/rom-mapper/badges/coverage.svg)][codeclimate]
14
+ [![Inline docs](http://inch-ci.org/github/rom-rb/rom-mapper.svg?branch=master)][inchpages]
15
+
16
+ ROM mapper component is a DSL for defining object mappers with pluggable mapping
17
+ backends. It is the default mapper in ROM.
14
18
 
15
- Mappers for [Ruby Object Mapper](http://rom-rb.org).
19
+ Resources:
16
20
 
17
- See ROM's [README](https://github.com/rom-rb/rom) for more information.
21
+ - [Guides](http://rom-rb.org/guides/basics/mappers/)
22
+ - [Importing Data with ROM and Transproc](http://solnic.eu/2015/07/15/importing-data-with-rom-and-transproc.html)
18
23
 
19
24
  ## License
20
25
 
21
- See LICENSE file.
26
+ See `LICENSE` file.
data/Rakefile CHANGED
@@ -1,7 +1,15 @@
1
- # encoding: utf-8
1
+ require "rspec/core/rake_task"
2
+ require "rake/testtask"
2
3
 
3
- require 'devtools'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task default: [:ci]
4
6
 
5
- Devtools.init_rake_tasks
7
+ desc 'Run specs in isolation'
8
+ task :"spec:isolation" do
9
+ FileList["spec/**/*_spec.rb"].each do |spec|
10
+ sh "rspec", spec
11
+ end
12
+ end
6
13
 
7
- require 'rspec/core/rake_task'
14
+ desc "Run CI tasks"
15
+ task ci: [:spec]
data/lib/rom-mapper.rb CHANGED
@@ -1,17 +1,8 @@
1
- # encoding: utf-8
1
+ require 'rom-support'
2
2
 
3
- require 'concord'
4
- require 'adamantium'
5
- require 'equalizer'
6
- require 'abstract_type'
7
-
8
- require 'rom/mapper/attribute'
9
- require 'rom/mapper/header'
10
-
11
- require 'rom/mapper/loader'
12
- require 'rom/mapper/loader/allocator'
13
- require 'rom/mapper/loader/attribute_writer'
14
- require 'rom/mapper/loader/object_builder'
15
-
16
- require 'rom/mapper/dumper'
17
3
  require 'rom/mapper'
4
+ require 'rom/processor/transproc'
5
+
6
+ module ROM
7
+ MapperMisconfiguredError = Class.new(StandardError)
8
+ end
data/lib/rom/header.rb ADDED
@@ -0,0 +1,195 @@
1
+ require 'equalizer'
2
+
3
+ require 'rom/header/attribute'
4
+
5
+ module ROM
6
+ # Header provides information about data mapping of a specific relation
7
+ #
8
+ # Processors use headers to build objects that process raw relations that go
9
+ # through mappers.
10
+ #
11
+ # @private
12
+ class Header
13
+ include Enumerable
14
+ include Equalizer.new(:attributes, :model)
15
+
16
+ # @return [Class] optional model associated with a header
17
+ #
18
+ # @api private
19
+ attr_reader :model
20
+
21
+ # @api private
22
+ attr_reader :reject_keys
23
+
24
+ # @api private
25
+ attr_reader :attributes
26
+
27
+ # @return [Hash] attribute key/name mapping for all primitive attributes
28
+ #
29
+ # @api private
30
+ attr_reader :mapping
31
+
32
+ # @return [Array] all attribute keys that are in a tuple
33
+ #
34
+ # @api private
35
+ attr_reader :tuple_keys
36
+
37
+ # @return [Array] all attribute names that are popping from a tuple
38
+ #
39
+ # @api private
40
+ attr_reader :pop_keys
41
+
42
+ # Coerce array with attribute definitions into a header object
43
+ #
44
+ # @param [Array<Array>] input attribute name/option pairs
45
+ #
46
+ # @param [Class] model optional
47
+ #
48
+ # @return [Header]
49
+ #
50
+ # @api private
51
+ def self.coerce(input, options = {})
52
+ if input.instance_of?(self)
53
+ input
54
+ else
55
+ attributes = input.each_with_object({}) { |pair, h|
56
+ h[pair.first] = Attribute.coerce(pair)
57
+ }
58
+
59
+ new(attributes, options)
60
+ end
61
+ end
62
+
63
+ # @api private
64
+ def initialize(attributes, options = {})
65
+ @options = options
66
+ @model = options[:model]
67
+ @reject_keys = options.fetch(:reject_keys, false)
68
+
69
+ @attributes = attributes
70
+ initialize_mapping
71
+ initialize_tuple_keys
72
+ initialize_pop_keys
73
+ end
74
+
75
+ # Iterate over attributes
76
+ #
77
+ # @yield [Attribute]
78
+ #
79
+ # @api private
80
+ def each
81
+ attributes.each_value { |attribute| yield(attribute) }
82
+ end
83
+
84
+ # Return if there are any aliased attributes
85
+ #
86
+ # @api private
87
+ def aliased?
88
+ any?(&:aliased?)
89
+ end
90
+
91
+ # Return attribute keys
92
+ #
93
+ # An attribute key corresponds to tuple attribute names
94
+ #
95
+ # @api private
96
+ def keys
97
+ attributes.keys
98
+ end
99
+
100
+ # Return attribute identified by its name
101
+ #
102
+ # @return [Attribute]
103
+ #
104
+ # @api private
105
+ def [](name)
106
+ attributes.fetch(name)
107
+ end
108
+
109
+ # Return all Combined attributes
110
+ #
111
+ # @return [Array<Combined>]
112
+ #
113
+ # @api private
114
+ def combined
115
+ by_type(Combined)
116
+ end
117
+
118
+ # Returns all attributes that require preprocessing
119
+ #
120
+ # @return [Array<Group,Fold>]
121
+ #
122
+ # @api private
123
+ def preprocessed
124
+ by_type(Group, Fold)
125
+ end
126
+
127
+ # Returns all attributes that require postprocessing
128
+ #
129
+ # @return [Array<Ungroup,Unfold>]
130
+ #
131
+ # @api private
132
+ def postprocessed
133
+ by_type(Ungroup, Unfold)
134
+ end
135
+
136
+ # Return all Wrap attributes
137
+ #
138
+ # @return [Array<Wrap>]
139
+ #
140
+ # @api private
141
+ def wraps
142
+ by_type(Wrap)
143
+ end
144
+
145
+ # Return all non-primitive attributes that don't require mapping
146
+ #
147
+ # @return [Array<Group,Fold,Ungroup,Unfold,Wrap,Unwrap>]
148
+ #
149
+ # @api private
150
+ def non_primitives
151
+ preprocessed + wraps
152
+ end
153
+
154
+ # Return all primitive attributes that require mapping
155
+ #
156
+ # @return [Array<Attribute>]
157
+ #
158
+ # @api private
159
+ def primitives
160
+ to_a - non_primitives
161
+ end
162
+
163
+ private
164
+
165
+ # Find all attribute matching specific attribute class (not kind)
166
+ #
167
+ # @return [Array<Attribute>]
168
+ #
169
+ # @api private
170
+ def by_type(*types)
171
+ select { |attribute| types.include?(attribute.class) }
172
+ end
173
+
174
+ # Set mapping hash from primitive attributes
175
+ #
176
+ # @api private
177
+ def initialize_mapping
178
+ @mapping = primitives.map(&:mapping).reduce(:merge) || {}
179
+ end
180
+
181
+ # Set all tuple keys from all attributes going deep into Wrap and Group too
182
+ #
183
+ # @api private
184
+ def initialize_tuple_keys
185
+ @tuple_keys = mapping.keys + non_primitives.flat_map(&:tuple_keys)
186
+ end
187
+
188
+ # Set all tuple keys from all attributes popping from Unwrap and Ungroup
189
+ #
190
+ # @api private
191
+ def initialize_pop_keys
192
+ @pop_keys = mapping.values + non_primitives.flat_map(&:tuple_keys)
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,184 @@
1
+ module ROM
2
+ class Header
3
+ # An attribute provides information about a specific attribute in a tuple
4
+ #
5
+ # This may include information about how an attribute should be renamed,
6
+ # or how its value should coerced.
7
+ #
8
+ # More complex attributes describe how an attribute should be transformed.
9
+ #
10
+ # @private
11
+ class Attribute
12
+ include Equalizer.new(:name, :key, :type)
13
+
14
+ # @return [Symbol] name of an attribute
15
+ #
16
+ # @api private
17
+ attr_reader :name
18
+
19
+ # @return [Symbol] key of an attribute that corresponds to tuple attribute
20
+ #
21
+ # @api private
22
+ attr_reader :key
23
+
24
+ # @return [Symbol] type identifier (defaults to :object)
25
+ #
26
+ # @api private
27
+ attr_reader :type
28
+
29
+ # @return [Hash] additional meta information
30
+ #
31
+ # @api private
32
+ attr_reader :meta
33
+
34
+ # Return attribute class for a given meta hash
35
+ #
36
+ # @param [Hash] meta hash with type information and optional transformation info
37
+ #
38
+ # @return [Class]
39
+ #
40
+ # @api private
41
+ def self.[](meta)
42
+ key = (meta.keys & TYPE_MAP.keys).first
43
+ TYPE_MAP.fetch(key || meta[:type], self)
44
+ end
45
+
46
+ # Coerce an array with attribute meta-data into an attribute object
47
+ #
48
+ # @param [Array<Symbol,Hash>] input attribute name/options pair
49
+ #
50
+ # @return [Attribute]
51
+ #
52
+ # @api private
53
+ def self.coerce(input)
54
+ name = input[0]
55
+ meta = (input[1] || {}).dup
56
+
57
+ meta[:type] ||= :object
58
+
59
+ if meta.key?(:header)
60
+ meta[:header] = Header.coerce(meta[:header], model: meta[:model])
61
+ end
62
+
63
+ self[meta].new(name, meta)
64
+ end
65
+
66
+ # @api private
67
+ def initialize(name, meta)
68
+ @name = name
69
+ @meta = meta
70
+ @key = meta.fetch(:from) { name }
71
+ @type = meta.fetch(:type)
72
+ end
73
+
74
+ # Return if an attribute has a specific type identifier
75
+ #
76
+ # @api private
77
+ def typed?
78
+ type != :object
79
+ end
80
+
81
+ # Return if an attribute should be aliased
82
+ #
83
+ # @api private
84
+ def aliased?
85
+ key != name
86
+ end
87
+
88
+ # Return :key-to-:name mapping hash
89
+ #
90
+ # @return [Hash]
91
+ #
92
+ # @api private
93
+ def mapping
94
+ { key => name }
95
+ end
96
+ end
97
+
98
+ # Embedded attribute is a special attribute type that has a header
99
+ #
100
+ # This is the base of complex attributes like Hash or Group
101
+ #
102
+ # @private
103
+ class Embedded < Attribute
104
+ include Equalizer.new(:name, :key, :type, :header)
105
+
106
+ # return [Header] header of an attribute
107
+ #
108
+ # @api private
109
+ attr_reader :header
110
+
111
+ # @api private
112
+ def initialize(*)
113
+ super
114
+ @header = meta.fetch(:header)
115
+ end
116
+
117
+ # Return tuple keys from the header
118
+ #
119
+ # @return [Array<Symbol>]
120
+ #
121
+ # @api private
122
+ def tuple_keys
123
+ header.tuple_keys
124
+ end
125
+
126
+ def pop_keys
127
+ header.pop_keys
128
+ end
129
+ end
130
+
131
+ # Array is an embedded attribute type
132
+ Array = Class.new(Embedded)
133
+
134
+ # Hash is an embedded attribute type
135
+ Hash = Class.new(Embedded)
136
+
137
+ # Combined is an embedded attribute type describing combination of multiple
138
+ # relations
139
+ Combined = Class.new(Embedded)
140
+
141
+ # Wrap is a special type of Hash attribute that requires wrapping
142
+ # transformation
143
+ Wrap = Class.new(Hash)
144
+
145
+ # Unwrap is a special type of Hash attribute that requires unwrapping
146
+ # transformation
147
+ Unwrap = Class.new(Hash)
148
+
149
+ # Group is a special type of Array attribute that requires grouping
150
+ # transformation
151
+ Group = Class.new(Array)
152
+
153
+ # Ungroup is a special type of Array attribute that requires ungrouping
154
+ # transformation
155
+ Ungroup = Class.new(Array)
156
+
157
+ # Fold is a special type of Array attribute that requires folding
158
+ # transformation
159
+ Fold = Class.new(Array)
160
+
161
+ # Unfold is a special type of Array attribute that requires unfolding
162
+ # transformation
163
+ Unfold = Class.new(Array)
164
+
165
+ # Exclude is a special type of Attribute to be removed
166
+ Exclude = Class.new(Attribute)
167
+
168
+ # TYPE_MAP is a (hash) map of ROM::Header identifiers to ROM::Header types
169
+ #
170
+ # @private
171
+ TYPE_MAP = {
172
+ combine: Combined,
173
+ wrap: Wrap,
174
+ unwrap: Unwrap,
175
+ group: Group,
176
+ ungroup: Ungroup,
177
+ fold: Fold,
178
+ unfold: Unfold,
179
+ hash: Hash,
180
+ array: Array,
181
+ exclude: Exclude
182
+ }
183
+ end
184
+ end