rom-mapper 0.5.1 → 1.0.0.beta1

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