realm-rom 0.7.3 → 0.7.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 631dd7891edbda2e66cc9d812434d1a2036197f80be79e25ea061d2f37b8f2d6
4
- data.tar.gz: 7d71ebd4ebc9d465fb36ee1d7b93b87d30166c635569aed0b0df9a1a425e3f08
3
+ metadata.gz: 443eba2e7ede9e2162eb8cbaa4324c6db29ba1bee69426a1e053629451957e94
4
+ data.tar.gz: 91c1ab5cf3f6b79f5205a25df6626b9c9164e4127569d984b66136887e564835
5
5
  SHA512:
6
- metadata.gz: b9a81123cb1e55f7c9173a2d4edd8e0da6a30d15f606d74789f64c7fb52aab3ff9f64fc4629371938ee1fd076d5557c48187f2a1bb6e23c982e7d2b573216901
7
- data.tar.gz: c2d44929520091709e83469ff68b2cdc52995eb5faa22dd4be223d7a083309b12597b4260f4063bfa23f7af978fd65654910282e2fd219ca9737301df7eaa668
6
+ metadata.gz: b6f8acb1cce4429e5e9b386aae5259bce4108ad001225004b88125c9bfaf3089bd89b4b494af7eaa40a2f988b2821d79e1252758898342e7312a6453c27610f9
7
+ data.tar.gz: 25920069362c88a229b32f555deadf380bbfc90df38cba030b3b20239329f39109437a0ff477ce8bebc1170a23593b0c736b73e238806381153d0bdbeb24c7ab
data/lib/realm-rom.rb ADDED
@@ -0,0 +1,16 @@
1
+ # rubocop:disable Naming/FileName
2
+ # frozen_string_literal: true
3
+
4
+ require 'zeitwerk'
5
+
6
+ loader = Zeitwerk::Loader.for_gem
7
+ loader.ignore(__FILE__)
8
+ loader.inflector.inflect('rom' => 'ROM')
9
+ loader.setup
10
+
11
+ require 'realm-core'
12
+ require 'rom'
13
+ require 'rom-sql'
14
+ require 'realm/rom/plugin'
15
+
16
+ # rubocop:enable Naming/FileName
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Realm
4
+ module ROM
5
+ class Gateway
6
+ def initialize(url:, root_module:, class_path:, migration_path:, **)
7
+ @url = url
8
+ @root_module = root_module
9
+ @class_path = class_path
10
+ @migration_path = migration_path
11
+ end
12
+
13
+ def health
14
+ issues = []
15
+ issues << 'Cannot connect to db' unless default_gateway.connection.test_connection
16
+ issues << 'Pending migrations' if default_gateway.migrator.pending?
17
+ HealthStatus.from_issues(issues)
18
+ end
19
+
20
+ def method_missing(...)
21
+ client.send(...)
22
+ end
23
+
24
+ def respond_to_missing?(...)
25
+ client.respond_to?(...)
26
+ end
27
+
28
+ private
29
+
30
+ def client
31
+ @client ||= ::ROM.container(config)
32
+ end
33
+
34
+ def config
35
+ ::ROM::Configuration.new(:sql, @url, **config_options).tap do |config|
36
+ config.auto_registration(@class_path, namespace: @root_module.to_s)
37
+ end
38
+ end
39
+
40
+ def config_options
41
+ { search_path: @root_module.to_s.underscore, migrator: { path: @migration_path } }
42
+ end
43
+
44
+ def default_gateway
45
+ client.gateways[:default]
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Realm
4
+ module ROM
5
+ class Plugin < Realm::Plugin
6
+ def self.setup(config, container)
7
+ return unless config.persistence_gateway[:type] == :rom
8
+
9
+ gateway = Gateway.new(config.persistence_gateway)
10
+ container.register('persistence.gateway', gateway)
11
+ container.register(:rom, gateway) # for backward compatibility as we access it a lot in tests
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+
5
+ module Realm
6
+ module ROM
7
+ module RakeTasks
8
+ # rubocop:disable Metrics/AbcSize, Metrics/BlockLength, Metrics/MethodLength
9
+ def self.setup(engine_name, engine_root: Rails.root.join('engines', engine_name.to_s),
10
+ db_url: ENV['DATABASE_URL'])
11
+ return unless db_url
12
+
13
+ options = { search_path: engine_name.to_s, migrator: { path: "#{engine_root}/db/migrate" } }
14
+ config = ::ROM::Configuration.new(:sql, db_url, options)
15
+ gateway = config.gateways[:default]
16
+
17
+ Rake.application.in_namespace(:db) do
18
+ Rake::Task.define_task(:init_schema) do
19
+ gateway.run "CREATE SCHEMA IF NOT EXISTS \"#{engine_name}\""
20
+ puts "<= #{engine_name}:db:init_schema executed"
21
+ end
22
+
23
+ Rake::Task.define_task(:drop_schema) do
24
+ gateway.run "DROP SCHEMA \"#{engine_name}\" CASCADE"
25
+ puts "<= #{engine_name}:db:drop_schema executed"
26
+ end
27
+
28
+ Rake.application.last_description = 'Perform migration reset (full erase and migration up)'
29
+ Rake::Task.define_task(:reset) do
30
+ gateway.run_migrations(target: 0)
31
+ gateway.run_migrations
32
+ puts "<= #{engine_name}:db:reset executed"
33
+ end
34
+
35
+ Rake.application.last_description = 'Migrate the database (options [version_number])]'
36
+ Rake::Task.define_task(:migrate, %i[version]) do |_, args|
37
+ version = args[:version]
38
+
39
+ if version.nil?
40
+ gateway.run_migrations
41
+ puts "<= #{engine_name}:db:migrate executed"
42
+ else
43
+ gateway.run_migrations(target: version.to_i)
44
+ puts "<= #{engine_name}:db:migrate version=[#{version}] executed"
45
+ end
46
+ end
47
+
48
+ Rake.application.last_description = 'Perform migration down (removes all tables)'
49
+ Rake::Task.define_task(:clean) do
50
+ gateway.run_migrations(target: 0)
51
+ puts "<= #{engine_name}:db:clean executed"
52
+ end
53
+
54
+ Rake.application.last_description = 'Create a migration (parameters: NAME, VERSION)'
55
+ Rake::Task.define_task(:create_migration, %i[name version]) do |_, args|
56
+ name, version = args.values_at(:name, :version)
57
+
58
+ if name.nil?
59
+ puts "No NAME specified. Example usage:
60
+ `rake #{engine_name}:db:create_migration[create_users]`"
61
+ exit
62
+ end
63
+
64
+ path = gateway.migrator.create_file(*[name, version].compact)
65
+ puts "<= migration file created #{path}"
66
+ end
67
+ end
68
+ end
69
+ # rubocop:enable Metrics/AbcSize, Metrics/BlockLength, Metrics/MethodLength
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Realm
4
+ module ROM
5
+ class ReadOnlyRelationWrapper
6
+ FORBIDDEN_METHODS = (::ROM::SQL::Relation::Writing.instance_methods(false) + [:command]).freeze
7
+
8
+ def initialize(relation)
9
+ @relation = relation
10
+ end
11
+
12
+ def method_missing(symbol, *args)
13
+ raise Persistence::RelationIsReadOnly, @relation if FORBIDDEN_METHODS.include?(symbol)
14
+
15
+ @relation.send(symbol, *args)
16
+ end
17
+
18
+ def respond_to_missing?(symbol)
19
+ !FORBIDDEN_METHODS.include?(symbol) && @relation.respond_to?(symbol)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Realm
4
+ module ROM
5
+ class ReadOnlyRepositoryWrapper
6
+ def initialize(repo)
7
+ @repo = repo.clone
8
+ @repo.define_singleton_method(:root) { ReadOnlyRelationWrapper.new(super()) }
9
+ end
10
+
11
+ def method_missing(name, *args, &block)
12
+ @repo.send(name, *args, &block)
13
+ rescue Persistence::RelationIsReadOnly
14
+ raise Persistence::RepositoryIsReadOnly, @repo
15
+ end
16
+
17
+ def respond_to_missing?(*args)
18
+ @repo.respond_to?(*args)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom-repository'
4
+
5
+ module Realm
6
+ module ROM
7
+ class Repository < ::ROM::Repository::Root
8
+ # Prevents leaking of persistence details into business logic
9
+ class Isolated
10
+ def initialize(repo)
11
+ @repo = repo
12
+ end
13
+
14
+ def method_missing(*args, &block)
15
+ result = @repo.send(*args, &block)
16
+ result.is_a?(::ROM::Relation) ? result.to_a : result
17
+ rescue ::ROM::SQL::UniqueConstraintError
18
+ raise Realm::Persistence::Conflict
19
+ end
20
+
21
+ def respond_to_missing?(*args)
22
+ @repo.respond_to?(*args)
23
+ end
24
+ end
25
+
26
+ def self.new(*)
27
+ Isolated.new(super)
28
+ end
29
+
30
+ def self.repo_name(value = :not_provided)
31
+ @repo_name = value.to_sym unless value == :not_provided
32
+ @repo_name = name.demodulize.underscore unless defined?(@repo_name)
33
+ @repo_name
34
+ end
35
+
36
+ def readonly
37
+ @readonly ||= ROM::ReadOnlyRepositoryWrapper.new(self)
38
+ end
39
+ end
40
+ end
41
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: realm-rom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 0.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - developers@reevoo.com
@@ -156,8 +156,13 @@ executables: []
156
156
  extensions: []
157
157
  extra_rdoc_files: []
158
158
  files:
159
- - README.md
160
- - Rakefile
159
+ - lib/realm-rom.rb
160
+ - lib/realm/rom/gateway.rb
161
+ - lib/realm/rom/plugin.rb
162
+ - lib/realm/rom/rake_tasks.rb
163
+ - lib/realm/rom/read_only_relation_wrapper.rb
164
+ - lib/realm/rom/read_only_repository_wrapper.rb
165
+ - lib/realm/rom/repository.rb
161
166
  homepage:
162
167
  licenses:
163
168
  - MIT
data/README.md DELETED
@@ -1,40 +0,0 @@
1
- # Realm
2
-
3
- Domain layer framework following Domain-driven/CQRS design principles.
4
-
5
- [![Build status](https://badge.buildkite.com/346cce75f6c31e0a41bb98b198e85eb6b722243624459fad9c.svg)](https://buildkite.com/reevoo/realm)
6
-
7
- ## Service layers
8
-
9
- We follow the standard MVC design pattern of Rails but giving the model layer more structure and guidance regarding where
10
- to put your code. The model is split into domain layer (using our [Realm](https://github.com/reevoo/smart-mono/tree/master/gems/realm) library)
11
- and persistence layer (using [ROM](https://rom-rb.org/) library). The individual components are explained in the following section.
12
-
13
- ![Service layers](https://confluence-connect.gliffy.net/embed/image/d02d04b1-5e40-415f-b7ba-3a631efa9bf3.png?utm_medium=live&utm_source=custom)
14
-
15
- Advanced components are shown in lighter color, those will be needed only later on as the service domain logic grows.
16
-
17
- ## Model layer components
18
-
19
- ![Service external components](https://confluence-connect.gliffy.net/embed/image/c593fcc2-304e-47c3-8e3c-b0cc09e0ed54.png?utm_medium=live&utm_source=custom)
20
-
21
- Each service has one **domain** module which consists of multiple [**aggregate**](https://martinfowler.com/bliki/DDD_Aggregate.html) modules.
22
- Aggregate is a cluster of domain objects that can be treated as a single unit. The only way for outer world to communicate
23
- with aggregate is by **queries** and **commands**. Query exposes aggregate's internal state and command changes it.
24
- The state of an aggregate is represented by tree of **entities** with one being the aggregate root and zero or more dependent
25
- entities with *belongs_to* relation to the root entity. The state of an aggregate (entity tree) is persisted
26
- and retrieved by **repository**. There is generally one repository per aggregate unless we split the read/write
27
- (query/command) persistence model for that particular domain. The repository uses **relations** to access the database
28
- tables. Each relation class represents one table.
29
-
30
-
31
- ## Where to put my code as it grows?
32
-
33
- TODO
34
-
35
-
36
- ## Roadmap
37
-
38
- - [ ] Support Ruby 3
39
- - [ ] Make it work outside of Rails engines
40
- - [ ] Support multiple persistence gateways in one runtime
data/Rakefile DELETED
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- begin
4
- require 'bundler/setup'
5
- rescue LoadError
6
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
- end
8
-
9
- require 'rdoc/task'
10
-
11
- RDoc::Task.new(:rdoc) do |rdoc|
12
- rdoc.rdoc_dir = 'rdoc'
13
- rdoc.title = 'Realm'
14
- rdoc.options << '--line-numbers'
15
- rdoc.rdoc_files.include('README.md')
16
- rdoc.rdoc_files.include('lib/**/*.rb')
17
- end
18
-
19
- require 'bundler/gem_tasks'