rom-mapper 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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