rom-repository 1.4.0 → 2.0.0.beta1

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 +4 -4
  2. data/CHANGELOG.md +14 -6
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +18 -1
  5. data/lib/rom-repository.rb +1 -2
  6. data/lib/rom/repository.rb +9 -216
  7. data/lib/rom/repository/class_interface.rb +16 -33
  8. data/lib/rom/repository/relation_reader.rb +46 -0
  9. data/lib/rom/repository/root.rb +3 -59
  10. data/lib/rom/repository/version.rb +1 -1
  11. metadata +9 -98
  12. data/.gitignore +0 -3
  13. data/.rspec +0 -3
  14. data/.travis.yml +0 -27
  15. data/.yardopts +0 -2
  16. data/Gemfile +0 -38
  17. data/Rakefile +0 -19
  18. data/lib/rom/open_struct.rb +0 -35
  19. data/lib/rom/repository/changeset.rb +0 -155
  20. data/lib/rom/repository/changeset/associated.rb +0 -100
  21. data/lib/rom/repository/changeset/create.rb +0 -16
  22. data/lib/rom/repository/changeset/delete.rb +0 -17
  23. data/lib/rom/repository/changeset/pipe.rb +0 -97
  24. data/lib/rom/repository/changeset/restricted.rb +0 -28
  25. data/lib/rom/repository/changeset/stateful.rb +0 -282
  26. data/lib/rom/repository/changeset/update.rb +0 -82
  27. data/lib/rom/repository/command_compiler.rb +0 -257
  28. data/lib/rom/repository/command_proxy.rb +0 -26
  29. data/lib/rom/repository/header_builder.rb +0 -65
  30. data/lib/rom/repository/mapper_builder.rb +0 -23
  31. data/lib/rom/repository/relation_proxy.rb +0 -337
  32. data/lib/rom/repository/relation_proxy/combine.rb +0 -320
  33. data/lib/rom/repository/relation_proxy/wrap.rb +0 -78
  34. data/lib/rom/repository/struct_builder.rb +0 -83
  35. data/lib/rom/struct.rb +0 -113
  36. data/log/.gitkeep +0 -0
  37. data/rom-repository.gemspec +0 -23
  38. data/spec/integration/changeset_spec.rb +0 -193
  39. data/spec/integration/command_macros_spec.rb +0 -191
  40. data/spec/integration/command_spec.rb +0 -228
  41. data/spec/integration/multi_adapter_spec.rb +0 -73
  42. data/spec/integration/repository/aggregate_spec.rb +0 -58
  43. data/spec/integration/repository_spec.rb +0 -406
  44. data/spec/integration/root_repository_spec.rb +0 -106
  45. data/spec/integration/typed_structs_spec.rb +0 -64
  46. data/spec/shared/database.rb +0 -79
  47. data/spec/shared/mappers.rb +0 -35
  48. data/spec/shared/models.rb +0 -41
  49. data/spec/shared/plugins.rb +0 -66
  50. data/spec/shared/relations.rb +0 -115
  51. data/spec/shared/repo.rb +0 -86
  52. data/spec/shared/seeds.rb +0 -30
  53. data/spec/shared/structs.rb +0 -140
  54. data/spec/spec_helper.rb +0 -83
  55. data/spec/support/mapper_registry.rb +0 -9
  56. data/spec/support/mutant.rb +0 -10
  57. data/spec/unit/changeset/associate_spec.rb +0 -120
  58. data/spec/unit/changeset/map_spec.rb +0 -111
  59. data/spec/unit/changeset_spec.rb +0 -186
  60. data/spec/unit/relation_proxy_spec.rb +0 -202
  61. data/spec/unit/repository/changeset_spec.rb +0 -197
  62. data/spec/unit/repository/inspect_spec.rb +0 -18
  63. data/spec/unit/repository/session_spec.rb +0 -251
  64. data/spec/unit/repository/transaction_spec.rb +0 -42
  65. data/spec/unit/session_spec.rb +0 -46
  66. data/spec/unit/struct_builder_spec.rb +0 -128
@@ -1,78 +0,0 @@
1
- module ROM
2
- class Repository
3
- class RelationProxy
4
- # Provides convenient methods for producing wrapped relations
5
- #
6
- # @api public
7
- module Wrap
8
- # Wrap other relations
9
- #
10
- # @example
11
- # tasks.wrap(owner: [users, user_id: :id])
12
- #
13
- # @param [Hash] options
14
- #
15
- # @return [RelationProxy]
16
- #
17
- # @api public
18
- def wrap(*names, **options)
19
- new_wraps = wraps_from_names(names) + wraps_from_options(options)
20
-
21
- relation = new_wraps.reduce(self) { |a, e|
22
- name = e.meta[:wrap_from_assoc] ? e.meta[:combine_name] : e.base_name.relation
23
- a.relation.for_wrap(e.meta.fetch(:keys), name)
24
- }
25
-
26
- __new__(relation, meta: { wraps: wraps + new_wraps })
27
- end
28
-
29
- # Shortcut to wrap parents
30
- #
31
- # @example
32
- # tasks.wrap_parent(owner: users)
33
- #
34
- # @return [RelationProxy]
35
- #
36
- # @api public
37
- def wrap_parent(options)
38
- wrap(
39
- options.each_with_object({}) { |(name, parent), h|
40
- keys = combine_keys(parent, relation, :children)
41
- h[name] = [parent, keys]
42
- }
43
- )
44
- end
45
-
46
- # Return a wrapped representation of a loading-proxy relation
47
- #
48
- # This will carry meta info used to produce a correct AST from a relation
49
- # so that correct mapper can be generated
50
- #
51
- # @return [RelationProxy]
52
- #
53
- # @api private
54
- def wrapped(name, keys, wrap_from_assoc = false)
55
- with(
56
- name: name,
57
- meta: {
58
- keys: keys, wrap_from_assoc: wrap_from_assoc, wrap: true, combine_name: name
59
- }
60
- )
61
- end
62
-
63
- # @api private
64
- def wraps_from_options(options)
65
- options.map { |(name, (relation, keys))| relation.wrapped(name, keys) }
66
- end
67
-
68
- # @api private
69
- def wraps_from_names(names)
70
- names.map { |name|
71
- assoc = associations[name]
72
- registry[assoc.target.relation].wrapped(name, assoc.combine_keys(__registry__), true)
73
- }
74
- end
75
- end
76
- end
77
- end
78
- end
@@ -1,83 +0,0 @@
1
- require 'dry/core/inflector'
2
- require 'dry/core/cache'
3
- require 'dry/core/class_builder'
4
-
5
- require 'rom/struct'
6
- require 'rom/open_struct'
7
- require 'rom/schema/attribute'
8
-
9
- module ROM
10
- class Repository
11
- # @api private
12
- class StructBuilder
13
- extend Dry::Core::Cache
14
-
15
- def call(*args)
16
- fetch_or_store(*args) do
17
- name, header = args
18
- attributes = visit(header).compact
19
-
20
- if attributes.empty?
21
- ROM::OpenStruct
22
- else
23
- build_class(name, ROM::Struct) do |klass|
24
- attributes.each do |(name, type)|
25
- klass.attribute(name, type)
26
- end
27
- end
28
- end
29
- end
30
- end
31
- alias_method :[], :call
32
-
33
- attr_reader :namespace
34
-
35
- def initialize(namespace = nil)
36
- @namespace = namespace || ROM::Struct
37
- end
38
-
39
- private
40
-
41
- def visit(ast)
42
- name, node = ast
43
- __send__("visit_#{name}", node)
44
- end
45
-
46
- def visit_header(node)
47
- node.map(&method(:visit))
48
- end
49
-
50
- def visit_relation(node)
51
- relation_name, meta, header = node
52
- name = meta[:combine_name] || relation_name.relation
53
-
54
- model = meta.fetch(:model) { call(name, header) }
55
-
56
- member =
57
- if model < Dry::Struct
58
- model
59
- else
60
- Dry::Types::Definition.new(model).constructor(&model.method(:new))
61
- end
62
-
63
- if meta[:combine_type] == :many
64
- [name, Types::Array.member(member)]
65
- else
66
- [name, member.optional]
67
- end
68
- end
69
-
70
- def visit_attribute(attr)
71
- [attr.aliased? && !attr.wrapped? ? attr.alias : attr.name, attr.to_read_type]
72
- end
73
-
74
- def build_class(name, parent, &block)
75
- Dry::Core::ClassBuilder.new(name: class_name(name), parent: parent, namespace: namespace).call(&block)
76
- end
77
-
78
- def class_name(name)
79
- Dry::Core::Inflector.classify(Dry::Core::Inflector.singularize(name))
80
- end
81
- end
82
- end
83
- end
data/lib/rom/struct.rb DELETED
@@ -1,113 +0,0 @@
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(method, *)
108
- super
109
- rescue NameError => error
110
- raise MissingAttribute.new("#{ error.message } (not loaded attribute?)")
111
- end
112
- end
113
- end
data/log/.gitkeep DELETED
File without changes
@@ -1,23 +0,0 @@
1
- require File.expand_path('../lib/rom/repository/version', __FILE__)
2
-
3
- Gem::Specification.new do |gem|
4
- gem.name = 'rom-repository'
5
- gem.summary = 'Repository abstraction for rom-rb'
6
- gem.description = 'rom-repository adds support for auto-mapping and commands on top of rom-rb relations'
7
- gem.author = 'Piotr Solnica'
8
- gem.email = 'piotr.solnica+oss@gmail.com'
9
- gem.homepage = 'http://rom-rb.org'
10
- gem.require_paths = ['lib']
11
- gem.version = ROM::Repository::VERSION.dup
12
- gem.files = `git ls-files`.split("\n").reject { |name| name.include?('benchmarks') || name.include?('examples') || name.include?('bin/console') }
13
- gem.test_files = `git ls-files -- {spec}/*`.split("\n")
14
- gem.license = 'MIT'
15
-
16
- gem.add_runtime_dependency 'rom', '~> 3.3'
17
- gem.add_runtime_dependency 'rom-mapper', '~> 0.5'
18
- gem.add_runtime_dependency 'dry-core', '~> 0.3', '>= 0.3.1'
19
- gem.add_runtime_dependency 'dry-struct', '~> 0.3'
20
-
21
- gem.add_development_dependency 'rake', '~> 11.2'
22
- gem.add_development_dependency 'rspec', '~> 3.5'
23
- end
@@ -1,193 +0,0 @@
1
- RSpec.describe 'Using changesets' do
2
- include_context 'database'
3
- include_context 'relations'
4
-
5
- before do
6
- module Test
7
- class User < Dry::Struct
8
- attribute :id, Dry::Types['strict.int']
9
- attribute :name, Dry::Types['strict.string']
10
- end
11
- end
12
-
13
- configuration.mappers do
14
- define(:users) do
15
- model Test::User
16
- register_as :user
17
- end
18
- end
19
- end
20
-
21
- describe 'Create' do
22
- subject(:repo) do
23
- Class.new(ROM::Repository[:users]) {
24
- relations :books, :posts
25
- commands :create, update: :by_pk
26
- }.new(rom)
27
- end
28
-
29
- let(:create_changeset) do
30
- Class.new(ROM::Changeset::Create)
31
- end
32
-
33
- let(:add_book_changeset) do
34
- Class.new(ROM::Changeset::Create[:books])
35
- end
36
-
37
- let(:update_changeset) do
38
- Class.new(ROM::Changeset::Update)
39
- end
40
-
41
- it 'can be passed to a command' do
42
- changeset = repo.changeset(name: "Jane Doe")
43
- command = repo.command(:create, repo.users)
44
- result = command.(changeset)
45
-
46
- expect(result.id).to_not be(nil)
47
- expect(result.name).to eql("Jane Doe")
48
- end
49
-
50
- it 'can be passed to a command graph' do
51
- changeset = repo.changeset(
52
- name: "Jane Doe", posts: [{ title: "Just Do It", alien: "or sutin" }]
53
- )
54
-
55
- command = repo.command(:create, repo.aggregate(:posts))
56
- result = command.(changeset)
57
-
58
- expect(result.id).to_not be(nil)
59
- expect(result.name).to eql("Jane Doe")
60
- expect(result.posts.size).to be(1)
61
- expect(result.posts[0].title).to eql("Just Do It")
62
- end
63
-
64
- it 'preprocesses data using changeset pipes' do
65
- changeset = repo.changeset(:books, title: "rom-rb is awesome").map(:add_timestamps)
66
- command = repo.command(:create, repo.books)
67
- result = command.(changeset)
68
-
69
- expect(result.id).to_not be(nil)
70
- expect(result.title).to eql("rom-rb is awesome")
71
- expect(result.created_at).to be_instance_of(Time)
72
- expect(result.updated_at).to be_instance_of(Time)
73
- end
74
-
75
- it 'preprocesses data using custom block' do
76
- changeset = repo.
77
- changeset(:books, title: "rom-rb is awesome").
78
- map { |tuple| tuple.merge(created_at: Time.now) }
79
-
80
- command = repo.command(:create, repo.books)
81
- result = command.(changeset)
82
-
83
- expect(result.id).to_not be(nil)
84
- expect(result.title).to eql("rom-rb is awesome")
85
- expect(result.created_at).to be_instance_of(Time)
86
- end
87
-
88
- it 'preprocesses data using built-in steps and custom block' do
89
- changeset = repo.
90
- changeset(:books, title: "rom-rb is awesome").
91
- extend(:touch) { |tuple| tuple.merge(created_at: Time.now) }
92
-
93
- command = repo.command(:create, repo.books)
94
- result = command.(changeset)
95
-
96
- expect(result.id).to_not be(nil)
97
- expect(result.title).to eql("rom-rb is awesome")
98
- expect(result.created_at).to be_instance_of(Time)
99
- expect(result.updated_at).to be_instance_of(Time)
100
- end
101
-
102
- it 'preserves relation mappers with create' do
103
- changeset = repo.
104
- changeset(create_changeset).
105
- new(repo.users.relation.as(:user)).
106
- data(name: 'Joe Dane')
107
-
108
- expect(changeset.commit).to eql(Test::User.new(id: 1, name: 'Joe Dane'))
109
- end
110
-
111
- it 'creates changesets for non-root relations' do
112
- repo.create(name: 'John Doe')
113
- changeset = repo.changeset(add_book_changeset).data(title: 'The War of the Worlds')
114
-
115
- expect(changeset.commit).
116
- to include(
117
- id: 1,
118
- title: 'The War of the Worlds',
119
- created_at: nil,
120
- updated_at: nil
121
- )
122
- end
123
- end
124
-
125
- describe 'Update' do
126
- subject(:repo) do
127
- Class.new(ROM::Repository[:books]) {
128
- commands :create, update: :by_pk
129
- }.new(rom)
130
- end
131
-
132
- it 'can be passed to a command' do
133
- book = repo.create(title: 'rom-rb is awesome')
134
-
135
- changeset = repo
136
- .changeset(book.id, title: 'rom-rb is awesome for real')
137
- .extend(:touch)
138
-
139
- expect(changeset.diff).to eql(title: 'rom-rb is awesome for real')
140
-
141
- result = repo.update(book.id, changeset)
142
-
143
- expect(result.id).to be(book.id)
144
- expect(result.title).to eql('rom-rb is awesome for real')
145
- expect(result.updated_at).to be_instance_of(Time)
146
- end
147
-
148
- it 'skips update execution with no diff' do
149
- book = repo.create(title: 'rom-rb is awesome')
150
-
151
- changeset = repo
152
- .changeset(book.id, title: 'rom-rb is awesome')
153
- .extend(:touch)
154
-
155
- expect(changeset).to_not be_diff
156
-
157
- result = repo.update(book.id, changeset)
158
-
159
- expect(result.id).to be(book.id)
160
- expect(result.title).to eql('rom-rb is awesome')
161
- expect(result.updated_at).to be(nil)
162
- end
163
-
164
- it 'works with mixed several class-level pipes' do
165
- book = repo.create(title: 'rom-rb is awesome')
166
-
167
- changeset_class = Class.new(ROM::Changeset::Update[:books]) do
168
- map { |title: | { title: title.upcase } }
169
- extend { |title: | { title: title.reverse } }
170
- end
171
-
172
- changeset = repo
173
- .changeset(changeset_class)
174
- .by_pk(book.id)
175
- .data(title: 'rom-rb is really awesome')
176
-
177
- expect(changeset.diff).to eql(title: 'ROM-RB IS REALLY AWESOME')
178
- expect(changeset.to_h).to eql(title: 'EMOSEWA YLLAER SI BR-MOR')
179
- end
180
-
181
- it 'works with mixed several instance-level pipes' do
182
- book = repo.create(title: 'rom-rb is awesome')
183
-
184
- changeset = repo.
185
- changeset(book.id, title: 'rom-rb is really awesome').
186
- map { |title: | { title: title.upcase } }.
187
- extend { |title: | { title: title.reverse } }
188
-
189
- expect(changeset.diff).to eql(title: 'ROM-RB IS REALLY AWESOME')
190
- expect(changeset.to_h).to eql(title: 'EMOSEWA YLLAER SI BR-MOR')
191
- end
192
- end
193
- end