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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 711c240df14665593d9d75aab68c4894ef711a36
4
- data.tar.gz: 96fdecc56bba304f3b088d86ada06f4cce5ed490
3
+ metadata.gz: dddebe835c3ce5f41a86e813e8e13334fa4a97dd
4
+ data.tar.gz: 2e241396a5a571534f25ed5cc5fbc171295b4a55
5
5
  SHA512:
6
- metadata.gz: b39af8bbfdb4ce683c1a110cce29188aa6047be279161d07221668e3f8c4ea53e855e941e1238211dc2debf0b5256a2c27094bc08cf2e4b4eab5cd39d1756d91
7
- data.tar.gz: 135de91ef60a3e2dfcf9fa9ea681ce77aed9efeada23c2fca013d3142428a66af9f8a879b45d8e8d940e6b30000afc4e4008289d8e9c873624452b452b373f9a
6
+ metadata.gz: a664f6746a7b14d2b8d57898bf3bc6376fae7c56e9360ad4b39eaa54efdce1616415f401202a921292ee0032eacdb457d723ed2621a10ad7ab98c8d5210cba09
7
+ data.tar.gz: 8b6228e180646d9eea2ab4e66fd83ef8807084f57da996df14ba00e4386e891f6ace3322a38aa6eefbc422ab3b235f6d1355487d3f4b010f1fe4689905e69976
@@ -1,3 +1,11 @@
1
+ # 0.5.1 2017-05-04
2
+
3
+ ### Changed
4
+
5
+ * The `dry-core` dependency has been relaxed (flash-gordon)
6
+
7
+ [Compare v0.5.0..v0.5.1](https://github.com/rom-rb/rom-mapper/compare/v0.5.0...v0.5.1)
8
+
1
9
  # 0.5.0 2017-01-29
2
10
 
3
11
  ### Changed
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Ruby Object Mapper Team
1
+ Copyright (c) 2013-2017 rom-rb team
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,25 +1,17 @@
1
1
  [gem]: https://rubygems.org/gems/rom-mapper
2
- [travis]: https://travis-ci.org/rom-rb/rom-mapper
3
2
  [gemnasium]: https://gemnasium.com/rom-rb/rom-mapper
4
- [codeclimate]: https://codeclimate.com/github/rom-rb/rom-mapper
5
- [inchpages]: http://inch-ci.org/github/rom-rb/rom-mapper
6
3
 
7
4
  # rom-mapper
8
5
 
9
6
  [![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
7
  [![Dependency Status](https://gemnasium.com/rom-rb/rom-mapper.svg)][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
8
 
16
9
  ROM mapper component is a DSL for defining object mappers with pluggable mapping
17
10
  backends. It is the default mapper in ROM.
18
11
 
19
12
  Resources:
20
13
 
21
- - [Guides](http://www.rubydoc.info/gems/rom-mapper/0.3.0/ROM/Mapper/AttributeDSL)
22
- - [Importing Data with ROM and Transproc](http://solnic.eu/2015/07/15/importing-data-with-rom-and-transproc.html)
14
+ - [API documentation](http://rubydoc.info/gems/rom-mapper)
23
15
 
24
16
  ## License
25
17
 
@@ -1,4 +1,4 @@
1
- require 'dry-equalizer'
1
+ require 'dry/equalizer'
2
2
 
3
3
  require 'rom/mapper'
4
4
  require 'rom/processor/transproc'
@@ -1,3 +1,5 @@
1
+ require 'dry/equalizer'
2
+
1
3
  module ROM
2
4
  class Header
3
5
  # An attribute provides information about a specific attribute in a tuple
@@ -1,12 +1,12 @@
1
- require 'dry/core/constants'
1
+ require 'rom/constants'
2
2
  require 'rom/mapper/dsl'
3
+ require 'rom/mapper/configuration_plugin'
3
4
 
4
5
  module ROM
5
6
  # Mapper is a simple object that uses transformers to load relations
6
7
  #
7
8
  # @private
8
9
  class Mapper
9
- include Dry::Core::Constants
10
10
  include DSL
11
11
  include Dry::Equalizer(:transformers, :header)
12
12
 
@@ -0,0 +1,37 @@
1
+ require 'dry/core/class_builder'
2
+
3
+ module ROM
4
+ class Mapper
5
+ # Setup DSL-specific mapper extensions
6
+ #
7
+ # @private
8
+ class Builder
9
+ # Generate a mapper subclass
10
+ #
11
+ # This is used by Setup#mappers DSL
12
+ #
13
+ # @api private
14
+ def self.build_class(name, mapper_registry, options = EMPTY_HASH, &block)
15
+ class_name = "ROM::Mapper[#{name}]"
16
+
17
+ parent = options[:parent]
18
+ inherit_header = options.fetch(:inherit_header) { ROM::Mapper.inherit_header }
19
+
20
+ parent_class =
21
+ if parent
22
+ mapper_registry.detect { |klass| klass.relation == parent }
23
+ else
24
+ ROM::Mapper
25
+ end
26
+
27
+ Dry::Core::ClassBuilder.new(name: class_name, parent: parent_class).call do |klass|
28
+ klass.register_as(name)
29
+ klass.relation(name)
30
+ klass.inherit_header(inherit_header)
31
+
32
+ klass.class_eval(&block) if block
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ require 'rom/mapper/mapper_dsl'
2
+
3
+ module ROM
4
+ class Mapper
5
+ # Model DSL allows setting a model class
6
+ #
7
+ # @private
8
+ module ConfigurationPlugin
9
+ # Mapper definition DSL used by Setup DSL
10
+ #
11
+ # @private
12
+
13
+ def self.apply(configuration, options = {})
14
+ configuration.extend Methods
15
+ configuration
16
+ end
17
+
18
+ module Methods
19
+ def mappers(&block)
20
+ register_mapper(*MapperDSL.new(self, mapper_classes, block).mapper_classes)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,43 @@
1
+ require 'rom/mapper/builder'
2
+
3
+ module ROM
4
+ class Mapper
5
+ # Mapper definition DSL used by Setup DSL
6
+ #
7
+ # @private
8
+ class MapperDSL
9
+ attr_reader :configuration, :mapper_classes, :defined_mappers
10
+
11
+ # @api private
12
+ def initialize(configuration, mapper_classes, block)
13
+ @configuration = configuration
14
+ @mapper_classes = mapper_classes
15
+ @defined_mappers = []
16
+
17
+ instance_exec(&block)
18
+
19
+ @mapper_classes = @defined_mappers
20
+ end
21
+
22
+ # Define a mapper class
23
+ #
24
+ # @param [Symbol] name of the mapper
25
+ # @param [Hash] options
26
+ #
27
+ # @return [Class]
28
+ #
29
+ # @api public
30
+ def define(name, options = EMPTY_HASH, &block)
31
+ @defined_mappers << Builder.build_class(name, (@mapper_classes + @defined_mappers), options, &block)
32
+ self
33
+ end
34
+
35
+ # TODO
36
+ #
37
+ # @api public
38
+ def register(relation, mappers)
39
+ configuration.register_mapper(relation => mappers)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,5 +1,5 @@
1
1
  module ROM
2
2
  class Mapper
3
- VERSION = '0.5.1'.freeze
3
+ VERSION = '1.0.0.beta1'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,71 @@
1
+ require 'rom/initializer'
2
+ require 'rom/mapper'
3
+ require 'rom/struct'
4
+ require 'rom/struct_compiler'
5
+ require 'rom/cache'
6
+
7
+ module ROM
8
+ # @api private
9
+ class MapperCompiler
10
+ extend Initializer
11
+
12
+ option :cache, reader: true, default: -> { Cache.new }
13
+
14
+ attr_reader :struct_compiler
15
+
16
+ def initialize(*args)
17
+ super
18
+ @struct_compiler = StructCompiler.new(cache: cache)
19
+ @cache = cache.namespaced(:mappers)
20
+ end
21
+
22
+ def call(ast)
23
+ cache.fetch_or_store(ast.hash) { Mapper.build(Header.coerce(*visit(ast))) }
24
+ end
25
+ alias_method :[], :call
26
+
27
+ private
28
+
29
+ def visit(node)
30
+ name, node = node
31
+ __send__("visit_#{name}", node)
32
+ end
33
+
34
+ def visit_relation(node)
35
+ rel_name, header, meta = node
36
+ name = meta[:combine_name] || meta[:alias] || rel_name
37
+ namespace = meta.fetch(:struct_namespace)
38
+
39
+ model = meta.fetch(:model) do
40
+ if meta[:combine_name]
41
+ false
42
+ else
43
+ struct_compiler[name, header, namespace]
44
+ end
45
+ end
46
+
47
+ options = [header.map(&method(:visit)), model: model]
48
+
49
+ if meta[:combine_type]
50
+ type = meta[:combine_type] == :many ? :array : :hash
51
+ keys = meta.fetch(:keys)
52
+
53
+ [name, combine: true, type: type, keys: keys, header: Header.coerce(*options)]
54
+ elsif meta[:wrap]
55
+ [name, wrap: true, type: :hash, header: Header.coerce(*options)]
56
+ else
57
+ options
58
+ end
59
+ end
60
+
61
+ def visit_attribute(node)
62
+ name, _, meta = node
63
+
64
+ if meta[:wrapped]
65
+ [name, from: meta[:alias]]
66
+ else
67
+ [name]
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,35 @@
1
+ module ROM
2
+ # ROM's open structs are used for relations with empty schemas.
3
+ # Such relations may exist in cases like using raw SQL strings
4
+ # where schema was not explicitly defined using `view` DSL.
5
+ #
6
+ # @api public
7
+ class OpenStruct
8
+ IVAR = -> v { :"@#{v}" }
9
+
10
+ # @api private
11
+ def initialize(attributes)
12
+ attributes.each do |key, value|
13
+ instance_variable_set(IVAR[key], value)
14
+ end
15
+ end
16
+
17
+ # @api private
18
+ def respond_to_missing?(meth, include_private = false)
19
+ super || instance_variables.include?(IVAR[meth])
20
+ end
21
+
22
+ private
23
+
24
+ # @api private
25
+ def method_missing(meth, *args, &block)
26
+ ivar = IVAR[meth]
27
+
28
+ if instance_variables.include?(ivar)
29
+ instance_variable_get(ivar)
30
+ else
31
+ super
32
+ end
33
+ end
34
+ end
35
+ end
@@ -28,6 +28,10 @@ module ROM
28
28
  tuple
29
29
  end
30
30
 
31
+ def self.get(arr, idx)
32
+ arr[idx]
33
+ end
34
+
31
35
  def self.filter_empty(arr)
32
36
  arr.reject { |row| row.values.all?(&:nil?) }
33
37
  end
@@ -158,7 +162,7 @@ module ROM
158
162
  op = with_row_proc(attribute) do |row_proc|
159
163
  array_proc =
160
164
  if attribute.type == :hash
161
- t(:map_array, row_proc) >> -> arr { arr.first }
165
+ t(:map_array, row_proc) >> t(:get, 0)
162
166
  else
163
167
  t(:map_array, row_proc)
164
168
  end
@@ -169,7 +173,7 @@ module ROM
169
173
  if op
170
174
  op
171
175
  elsif attribute.type == :hash
172
- t(:map_value, attribute.name, -> arr { arr.first })
176
+ t(:map_value, attribute.name, t(:get, 0))
173
177
  end
174
178
  end
175
179
 
@@ -0,0 +1,113 @@
1
+ require 'dry/struct'
2
+
3
+ module ROM
4
+ # Simple data-struct class
5
+ #
6
+ # ROM structs are plain data structures loaded by repositories.
7
+ # They implement Hash protocol which means that they can be used
8
+ # in places where Hash-like objects are supported.
9
+ #
10
+ # Repositories define subclasses of ROM::Struct automatically, they are
11
+ # defined in the ROM::Struct namespace by default, but you set it up
12
+ # to use your namespace/module as well.
13
+ #
14
+ # Structs are based on dry-struct gem, they include `schema` with detailed information
15
+ # about attribute types returned from relations, thus can be introspected to build
16
+ # additional functionality when desired.
17
+ #
18
+ # There is a caveat you should know about when working with structs. Struct classes
19
+ # have names but at the same time they're anonymous, i.e. you can't get the User struct class
20
+ # with ROM::Struct::User. ROM will create as many struct classes for User as needed,
21
+ # they all will have the same name and ROM::Struct::User will be the common parent class for
22
+ # them. Combined with the ability to provide your own namespace for structs this enables to
23
+ # pre-define the parent class.
24
+ #
25
+ # @example accessing relation struct model
26
+ # rom = ROM.container(:sql, 'sqlite::memory') do |conf|
27
+ # conf.default.create_table(:users) do
28
+ # primary_key :id
29
+ # column :name, String
30
+ # end
31
+ # end
32
+ #
33
+ # class UserRepo < ROM::Repository[:users]
34
+ # end
35
+ #
36
+ # user_repo = UserRepo.new(rom)
37
+ #
38
+ # # get auto-generated User struct
39
+ # model = user_repo.users.mapper.model
40
+ # # => ROM::Struct::User
41
+ #
42
+ # # see struct's schema attributes
43
+ #
44
+ # # model.schema[:id]
45
+ # # => #<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=Integer options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#gt?> options={:args=>[0]}>, :meta=>{:primary_key=>true, :name=>:id, :source=>ROM::Relation::Name(users)}} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#gt?> options={:args=>[0]}>>
46
+ #
47
+ # model.schema[:name]
48
+ # # => #<Dry::Types::Sum left=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=NilClass options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>> right=#<Dry::Types::Definition primitive=String options={}> options={:meta=>{:name=>:name, :source=>ROM::Relation::Name(users)}}>
49
+ #
50
+ # @example passing a namespace with an existing parent class
51
+ # module Entities
52
+ # class User < ROM::Struct
53
+ # def upcased_name
54
+ # name.upcase
55
+ # end
56
+ # end
57
+ # end
58
+ #
59
+ # class UserRepo < ROM::Repository[:users]
60
+ # struct_namespace Entities
61
+ # end
62
+ #
63
+ # user_repo = UserRepo.new(rom)
64
+ # user = user_repo.users.by_pk(1).one!
65
+ # user.name # => "Jane"
66
+ # user.upcased_name # => "JANE"
67
+ #
68
+ # @see http://dry-rb.org/gems/dry-struct dry-struct
69
+ # @see http://dry-rb.org/gems/dry-types dry-types
70
+ #
71
+ # @api public
72
+ class Struct < Dry::Struct
73
+ MissingAttribute = Class.new(NameError)
74
+
75
+ # Returns a short string representation
76
+ #
77
+ # @return [String]
78
+ #
79
+ # @api public
80
+ def to_s
81
+ "#<#{self.class}:0x#{(object_id << 1).to_s(16)}>"
82
+ end
83
+
84
+ # Return attribute value
85
+ #
86
+ # @param [Symbol] name The attribute name
87
+ #
88
+ # @api public
89
+ def fetch(name)
90
+ __send__(name)
91
+ end
92
+
93
+ if RUBY_VERSION < '2.3'
94
+ # @api private
95
+ def respond_to?(*)
96
+ super
97
+ end
98
+ else
99
+ # @api private
100
+ def respond_to_missing?(*)
101
+ super
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def method_missing(*)
108
+ super
109
+ rescue NameError => error
110
+ raise MissingAttribute.new("#{ error.message } (not loaded attribute?)")
111
+ end
112
+ end
113
+ end