realm-rom 0.7.3 → 0.7.4

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
  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'