rom-mapper 0.5.1 → 1.0.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.
@@ -0,0 +1,94 @@
1
+ require 'dry/core/inflector'
2
+ require 'dry/core/class_builder'
3
+ require 'dry/types/compiler'
4
+
5
+ require 'rom/initializer'
6
+ require 'rom/types'
7
+ require 'rom/cache'
8
+ require 'rom/struct'
9
+ require 'rom/open_struct'
10
+ require 'rom/schema/attribute'
11
+
12
+ module ROM
13
+ # @api private
14
+ class StructCompiler < Dry::Types::Compiler
15
+ extend Initializer
16
+
17
+ param :registry, default: -> { Dry::Types }
18
+ option :cache, default: -> { Cache.new }
19
+
20
+ def initialize(*args)
21
+ super
22
+ @cache = cache.namespaced(:structs)
23
+ end
24
+
25
+ def call(*args)
26
+ cache.fetch_or_store(args.hash) do
27
+ name, header, ns = args
28
+ attributes = header.map(&method(:visit)).compact
29
+
30
+ if attributes.empty?
31
+ ROM::OpenStruct
32
+ else
33
+ build_class(name, ROM::Struct, ns) do |klass|
34
+ attributes.each do |(name, type)|
35
+ klass.attribute(name, type)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ alias_method :[], :call
42
+
43
+ private
44
+
45
+ def visit_relation(node)
46
+ _, header, meta = node
47
+ name = meta[:combine_name] || meta[:alias]
48
+ namespace = meta.fetch(:struct_namespace)
49
+
50
+ model = meta[:model] || call(name, header, namespace)
51
+
52
+ member =
53
+ if model < Dry::Struct
54
+ model
55
+ else
56
+ Dry::Types::Definition.new(model).constructor(&model.method(:new))
57
+ end
58
+
59
+ if meta[:combine_type] == :many
60
+ [name, Types::Array.member(member)]
61
+ else
62
+ [name, member.optional]
63
+ end
64
+ end
65
+
66
+ def visit_attribute(node)
67
+ name, type, meta = node
68
+
69
+ [meta[:alias] && !meta[:wrapped] ? meta[:alias] : name, visit(type).meta(meta)]
70
+ end
71
+
72
+ def visit_constructor(node)
73
+ definition, fn_register_name, meta = node
74
+
75
+ visit(definition)
76
+ end
77
+
78
+ def visit_constrained(node)
79
+ definition, rule = node
80
+
81
+ visit(definition)
82
+ end
83
+
84
+ def build_class(name, parent, ns, &block)
85
+ Dry::Core::ClassBuilder.
86
+ new(name: class_name(name), parent: parent, namespace: ns).
87
+ call(&block)
88
+ end
89
+
90
+ def class_name(name)
91
+ Dry::Core::Inflector.classify(Dry::Core::Inflector.singularize(name))
92
+ end
93
+ end
94
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rom-mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 1.0.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-03 00:00:00.000000000 Z
11
+ date: 2017-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-equalizer
@@ -30,20 +30,20 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.2'
33
+ version: '0.3'
34
34
  - - ">="
35
35
  - !ruby/object:Gem::Version
36
- version: 0.2.3
36
+ version: 0.3.1
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - "~>"
42
42
  - !ruby/object:Gem::Version
43
- version: '0.2'
43
+ version: '0.3'
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 0.2.3
46
+ version: 0.3.1
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: transproc
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -86,45 +86,33 @@ dependencies:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
88
  version: '3.5'
89
- description: ROM mapper component
90
- email: piotr.solnica@gmail.com
89
+ description: Standalone data mappers integrated with rom-rb
90
+ email: piotr.solnica+oss@gmail.com
91
91
  executables: []
92
92
  extensions: []
93
93
  extra_rdoc_files: []
94
94
  files:
95
- - ".gitignore"
96
- - ".rspec"
97
- - ".ruby-gemset"
98
- - ".travis.yml"
99
95
  - CHANGELOG.md
100
- - Gemfile
101
- - Guardfile
102
96
  - LICENSE
103
97
  - README.md
104
- - Rakefile
105
98
  - lib/rom-mapper.rb
106
99
  - lib/rom/header.rb
107
100
  - lib/rom/header/attribute.rb
108
101
  - lib/rom/mapper.rb
109
102
  - lib/rom/mapper/attribute_dsl.rb
103
+ - lib/rom/mapper/builder.rb
104
+ - lib/rom/mapper/configuration_plugin.rb
110
105
  - lib/rom/mapper/dsl.rb
106
+ - lib/rom/mapper/mapper_dsl.rb
111
107
  - lib/rom/mapper/model_dsl.rb
112
108
  - lib/rom/mapper/version.rb
109
+ - lib/rom/mapper_compiler.rb
113
110
  - lib/rom/model_builder.rb
111
+ - lib/rom/open_struct.rb
114
112
  - lib/rom/processor.rb
115
113
  - lib/rom/processor/transproc.rb
116
- - rakelib/benchmark.rake
117
- - rakelib/mutant.rake
118
- - rakelib/rubocop.rake
119
- - rom-mapper.gemspec
120
- - spec/integration/mapper_spec.rb
121
- - spec/spec_helper.rb
122
- - spec/support/constant_leak_finder.rb
123
- - spec/support/mutant.rb
124
- - spec/unit/rom/mapper/dsl_spec.rb
125
- - spec/unit/rom/mapper/model_dsl_spec.rb
126
- - spec/unit/rom/mapper_spec.rb
127
- - spec/unit/rom/processor/transproc_spec.rb
114
+ - lib/rom/struct.rb
115
+ - lib/rom/struct_compiler.rb
128
116
  homepage: http://rom-rb.org
129
117
  licenses:
130
118
  - MIT
@@ -140,13 +128,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
140
128
  version: '0'
141
129
  required_rubygems_version: !ruby/object:Gem::Requirement
142
130
  requirements:
143
- - - ">="
131
+ - - ">"
144
132
  - !ruby/object:Gem::Version
145
- version: '0'
133
+ version: 1.3.1
146
134
  requirements: []
147
135
  rubyforge_project:
148
- rubygems_version: 2.6.11
136
+ rubygems_version: 2.6.12
149
137
  signing_key:
150
138
  specification_version: 4
151
- summary: ROM mapper component
139
+ summary: Standalone data mappers integrated with rom-rb
152
140
  test_files: []
data/.gitignore DELETED
@@ -1,19 +0,0 @@
1
- log
2
- *.rbx
3
- *.rbc
4
- Gemfile.lock
5
- vendor/
6
- tmp/
7
- doc/
8
- coverage/
9
- measurements/
10
- graphviz/
11
- .yardoc/
12
- .bundle/
13
- *.png
14
- Vagrantfile
15
- .vagrant
16
-
17
- # spiking area
18
- scratchpad.rb
19
- .ruby-version
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --color
2
- --order random
3
- --warnings
@@ -1 +0,0 @@
1
- rom
@@ -1,23 +0,0 @@
1
- language: ruby
2
- dist: trusty
3
- sudo: false
4
- cache: bundler
5
- bundler_args: --without sql benchmarks console tools
6
- script: "bundle exec rake ci"
7
- after_success:
8
- - '[ -d coverage ] && bundle exec codeclimate-test-reporter'
9
- rvm:
10
- - 2.2.7
11
- - 2.3.4
12
- - 2.4.1
13
- - jruby-9.1.8.0
14
- env:
15
- global:
16
- - JRUBY_OPTS='--dev -J-Xmx1024M'
17
- notifications:
18
- webhooks:
19
- urls:
20
- - https://webhooks.gitter.im/e/39e1225f489f38b0bd09
21
- on_success: change
22
- on_failure: always
23
- on_start: false
data/Gemfile DELETED
@@ -1,31 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- group :test do
6
- gem 'anima', '~> 0.3.0'
7
- gem 'virtus'
8
- gem 'inflecto', '~> 0.0', '>= 0.0.2'
9
-
10
- gem 'codeclimate-test-reporter', require: false
11
- gem 'simplecov', require: false
12
- end
13
-
14
- group :benchmarks do
15
- gem 'benchmark-ips', '~> 2.2'
16
- end
17
-
18
- group :tools do
19
- gem 'rubocop', '~> 0.31'
20
-
21
- gem 'guard'
22
- gem 'guard-rspec'
23
- gem 'guard-rubocop'
24
-
25
- gem 'byebug'
26
-
27
- platform :mri do
28
- gem 'mutant', '~> 0.8.0'
29
- gem 'mutant-rspec'
30
- end
31
- end
data/Guardfile DELETED
@@ -1,19 +0,0 @@
1
- guard :rspec do
2
- #run all specs if configuration is modified
3
- watch('Guardfile') { 'spec' }
4
- watch('Gemfile.lock') { 'spec' }
5
- watch('spec/spec_helper.rb') { 'spec' }
6
-
7
- # run all specs if supporting files files are modified
8
- watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec' }
9
-
10
- # run unit specs if associated lib code is modified
11
- watch(%r{\Alib/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}"] }
12
- watch(%r{\Alib/(.+)/support/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}/#{m[2]}"] }
13
- watch("lib/#{File.basename(File.expand_path('../', __FILE__))}.rb") { 'spec' }
14
-
15
- # run a spec if it is modified
16
- watch(%r{\Aspec/(?:unit|integration)/.+_spec\.rb\z})
17
-
18
- notification :tmux, :display_message => true
19
- end
data/Rakefile DELETED
@@ -1,16 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
- require "rake/testtask"
4
-
5
- RSpec::Core::RakeTask.new(:spec)
6
- task default: [:ci]
7
-
8
- desc 'Run specs in isolation'
9
- task :"spec:isolation" do
10
- FileList["spec/**/*_spec.rb"].each do |spec|
11
- sh "rspec", spec
12
- end
13
- end
14
-
15
- desc "Run CI tasks"
16
- task ci: [:spec]
@@ -1,15 +0,0 @@
1
- desc "Run benchmarks (tweak count via COUNT envvar)"
2
- task :benchmark do
3
- FileList["benchmarks/**/*_bench.rb"].each do |bench|
4
- sh "ruby #{bench}"
5
- end
6
- end
7
-
8
- namespace :benchmark do
9
- desc "Verify benchmarks"
10
- task :verify do
11
- ENV['VERIFY'] = "true"
12
- ENV['COUNT'] = "1"
13
- Rake::Task[:benchmark].invoke
14
- end
15
- end
@@ -1,16 +0,0 @@
1
- desc "Run mutant against a specific subject"
2
- task :mutant do
3
- subject = ARGV.last
4
- if subject == 'mutant'
5
- abort "usage: rake mutant SUBJECT\nexample: rake mutant ROM::Header"
6
- else
7
- opts = {
8
- 'include' => 'lib',
9
- 'require' => 'rom',
10
- 'use' => 'rspec',
11
- 'ignore-subject' => "#{subject}#respond_to_missing?"
12
- }.to_a.map { |k, v| "--#{k} #{v}" }.join(' ')
13
-
14
- exec("bundle exec mutant #{opts} #{subject}")
15
- end
16
- end
@@ -1,18 +0,0 @@
1
- begin
2
- require "rubocop/rake_task"
3
-
4
- Rake::Task[:default].enhance [:rubocop]
5
-
6
- RuboCop::RakeTask.new do |task|
7
- task.options << "--display-cop-names"
8
- end
9
-
10
- namespace :rubocop do
11
- desc 'Generate a configuration file acting as a TODO list.'
12
- task :auto_gen_config do
13
- exec "bundle exec rubocop --auto-gen-config"
14
- end
15
- end
16
-
17
- rescue LoadError
18
- end
@@ -1,22 +0,0 @@
1
- require File.expand_path('../lib/rom/mapper/version', __FILE__)
2
-
3
- Gem::Specification.new do |gem|
4
- gem.name = "rom-mapper"
5
- gem.description = "ROM mapper component"
6
- gem.summary = gem.description
7
- gem.authors = 'Piotr Solnica'
8
- gem.email = 'piotr.solnica@gmail.com'
9
- gem.homepage = 'http://rom-rb.org'
10
- gem.require_paths = [ 'lib' ]
11
- gem.version = ROM::Mapper::VERSION.dup
12
- gem.files = `git ls-files`.split("\n")
13
- gem.test_files = `git ls-files -- {spec}/*`.split("\n")
14
- gem.license = 'MIT'
15
-
16
- gem.add_dependency 'dry-equalizer', '~> 0.2'
17
- gem.add_dependency 'dry-core', '~> 0.2', '>= 0.2.3'
18
- gem.add_dependency 'transproc', '~> 1.0'
19
-
20
- gem.add_development_dependency 'rake', '~> 11.3'
21
- gem.add_development_dependency 'rspec', '~> 3.5'
22
- end
@@ -1,113 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe ROM::Mapper do
4
- let(:mapper_klass) do
5
- Class.new(ROM::Mapper).tap do |mapper_klass|
6
- mapper_klass.class_eval(&mapper_body)
7
- end
8
- end
9
- let(:mapper) { mapper_klass.build }
10
-
11
- subject { mapper.call(tuples) }
12
-
13
- describe '.attribute' do
14
- context 'with block' do
15
- let(:tuples) { [{ key: 'bar' }] }
16
- let(:results) { [{ key: 'foo_bar' }] }
17
- let(:mapper_body) do
18
- proc do
19
- attribute(:key) { |key| [prefix, key].join('_') }
20
-
21
- def prefix
22
- 'foo'
23
- end
24
- end
25
- end
26
-
27
- it 'creates the attribute from the proc with the mapper as the binding' do
28
- is_expected.to match_array(results)
29
- end
30
- end
31
-
32
- context 'when copying aliased keys to multiple attributes' do
33
- let(:tuples) { [{ key: 'bar' }] }
34
- let(:results) { [{ key: 'bar', key2: 'bar', key3: 'bar' }] }
35
- let(:mapper_body) do
36
- proc do
37
- copy_keys true
38
- attribute([:key2, :key3], from: :key)
39
- end
40
- end
41
-
42
- it 'creates attributes by copying keys rather than renaming' do
43
- is_expected.to match_array(results)
44
- end
45
- end
46
- end
47
-
48
- describe '.embedded' do
49
- context 'with block' do
50
- let(:tuples) { [{ items: { key: 'bar' } }] }
51
- let(:results) { [{ items: { key: 'foo_bar' } }] }
52
- let(:mapper_body) do
53
- proc do
54
- embedded :items, type: :hash do
55
- attribute(:key) { |key| [prefix, key].join('_') }
56
- end
57
-
58
- def prefix
59
- 'foo'
60
- end
61
- end
62
- end
63
-
64
- it 'creates the attribute from the proc with the mapper as the binding' do
65
- is_expected.to match_array(results)
66
- end
67
- end
68
- end
69
-
70
- describe '.wrap' do
71
- context 'attribute with block' do
72
- let(:tuples) { [{ key: 'bar' }] }
73
- let(:results) { [{ items: { key: 'foo_bar' } }] }
74
- let(:mapper_body) do
75
- proc do
76
- wrap :items do
77
- attribute(:key) { |key| [prefix, key].join('_') }
78
- end
79
-
80
- def prefix
81
- 'foo'
82
- end
83
- end
84
- end
85
-
86
- it 'creates the attribute from the proc with the mapper as the binding' do
87
- is_expected.to match_array(results)
88
- end
89
- end
90
- end
91
-
92
- describe '.unwrap' do
93
- context 'attribute with block' do
94
- let(:tuples) { [{ items: { key: 'bar' } }] }
95
- let(:results) { [{ key: 'foo_bar' }] }
96
- let(:mapper_body) do
97
- proc do
98
- unwrap :items do
99
- attribute(:key) { |key| [prefix, key].join('_') }
100
- end
101
-
102
- def prefix
103
- 'foo'
104
- end
105
- end
106
- end
107
-
108
- it 'creates the attribute from the proc with the mapper as the binding' do
109
- is_expected.to match_array(results)
110
- end
111
- end
112
- end
113
- end