upgrow 0.0.2 → 0.0.3
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 +4 -4
- data/Rakefile +1 -1
- data/lib/upgrow.rb +2 -8
- data/lib/upgrow/action.rb +66 -16
- data/lib/upgrow/actions.rb +31 -0
- data/lib/upgrow/active_record_adapter.rb +24 -15
- data/lib/upgrow/active_record_schema.rb +63 -0
- data/lib/upgrow/basic_model.rb +64 -0
- data/lib/upgrow/basic_repository.rb +49 -22
- data/lib/upgrow/error.rb +19 -0
- data/lib/upgrow/immutable_object.rb +26 -21
- data/lib/upgrow/input.rb +7 -0
- data/lib/upgrow/model.rb +12 -12
- data/lib/upgrow/model_schema.rb +31 -0
- data/lib/upgrow/repository.rb +3 -0
- data/lib/upgrow/result.rb +18 -55
- data/lib/upgrow/schema.rb +33 -0
- data/test/application_system_test_case.rb +11 -0
- data/test/dummy/app/actions/application_action.rb +10 -0
- data/test/dummy/app/actions/articles/create_action.rb +15 -0
- data/test/dummy/app/actions/articles/destroy_action.rb +9 -0
- data/test/dummy/app/actions/articles/edit_action.rb +12 -0
- data/test/dummy/app/actions/articles/index_action.rb +11 -0
- data/test/dummy/app/actions/articles/new_action.rb +8 -0
- data/test/dummy/app/actions/articles/show_action.rb +11 -0
- data/test/dummy/app/actions/articles/update_action.rb +15 -0
- data/test/dummy/app/actions/comments/create_action.rb +15 -0
- data/test/dummy/app/actions/comments/destroy_action.rb +9 -0
- data/test/dummy/app/actions/comments/new_action.rb +8 -0
- data/test/dummy/app/actions/sessions/create_action.rb +23 -0
- data/test/dummy/app/actions/sessions/destroy_action.rb +6 -0
- data/test/dummy/app/actions/sessions/new_action.rb +8 -0
- data/test/dummy/app/actions/user_action.rb +10 -0
- data/test/dummy/app/actions/users/create_action.rb +13 -0
- data/test/dummy/app/actions/users/new_action.rb +8 -0
- data/test/dummy/app/controllers/application_controller.rb +10 -0
- data/test/dummy/app/controllers/articles_controller.rb +32 -28
- data/test/dummy/app/controllers/comments_controller.rb +41 -0
- data/test/dummy/app/controllers/sessions_controller.rb +34 -0
- data/test/dummy/app/controllers/users_controller.rb +29 -0
- data/test/dummy/app/helpers/application_helper.rb +27 -3
- data/test/dummy/app/helpers/users_helper.rb +15 -0
- data/test/dummy/app/inputs/article_input.rb +3 -0
- data/test/dummy/app/inputs/comment_input.rb +11 -0
- data/test/dummy/app/inputs/session_input.rb +9 -0
- data/test/dummy/app/inputs/user_input.rb +9 -0
- data/test/dummy/app/models/article.rb +0 -2
- data/test/dummy/app/models/comment.rb +4 -0
- data/test/dummy/app/models/user.rb +4 -0
- data/test/dummy/app/records/article_record.rb +2 -0
- data/test/dummy/app/records/comment_record.rb +7 -0
- data/test/dummy/app/records/user_record.rb +9 -0
- data/test/dummy/app/repositories/article_repository.rb +26 -0
- data/test/dummy/app/repositories/comment_repository.rb +3 -0
- data/test/dummy/app/repositories/user_repository.rb +12 -0
- data/test/dummy/config/routes.rb +6 -1
- data/test/dummy/db/migrate/20210320140432_create_comments.rb +12 -0
- data/test/dummy/db/migrate/20210409164927_create_users.rb +22 -0
- data/test/dummy/db/schema.rb +24 -1
- data/test/system/articles_test.rb +87 -29
- data/test/system/comments_test.rb +81 -0
- data/test/system/guest_user_test.rb +14 -0
- data/test/system/sign_in_test.rb +57 -0
- data/test/system/sign_out_test.rb +19 -0
- data/test/system/sign_up_test.rb +38 -0
- data/test/test_helper.rb +6 -1
- data/test/upgrow/action_test.rb +101 -9
- data/test/upgrow/actions_test.rb +24 -0
- data/test/upgrow/active_record_adapter_test.rb +12 -17
- data/test/upgrow/active_record_schema_test.rb +92 -0
- data/test/upgrow/basic_model_test.rb +95 -0
- data/test/upgrow/basic_repository_test.rb +48 -27
- data/test/upgrow/immutable_object_test.rb +43 -7
- data/test/upgrow/input_test.rb +19 -1
- data/test/upgrow/model_schema_test.rb +44 -0
- data/test/upgrow/model_test.rb +48 -11
- data/test/upgrow/result_test.rb +19 -64
- data/test/upgrow/schema_test.rb +44 -0
- metadata +128 -50
- data/test/dummy/app/actions/create_article_action.rb +0 -13
- data/test/dummy/app/actions/delete_article_action.rb +0 -7
- data/test/dummy/app/actions/edit_article_action.rb +0 -10
- data/test/dummy/app/actions/list_articles_action.rb +0 -8
- data/test/dummy/app/actions/show_article_action.rb +0 -10
- data/test/dummy/app/actions/update_article_action.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3238cf0308b8e81b0349792dc104880479702dc5c802a8985d2f37461fb1029f
|
4
|
+
data.tar.gz: 3c06e81104d588ae28d500d4db9946e16afeee449eb7b58a70700a8e3f6b7bb1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7f23d2e55b0b06d99c168353c99e1b13e3ee4c0d9a12371e5f6771c7bc9dfae347512c583d716188454b634d54d57149e24b98cf203738d5aee718148405f29
|
7
|
+
data.tar.gz: 646705859b0fb813b215081f1f52058b2b36b2e15b2a0b81cdeb0dfd537ebe30e84c2846a5b37e99d9646e9706015ded057f365100798bdec360918c8936ae5a
|
data/Rakefile
CHANGED
data/lib/upgrow.rb
CHANGED
@@ -1,16 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_model'
|
4
|
-
|
5
3
|
require_relative 'upgrow/action'
|
6
|
-
require_relative 'upgrow/
|
7
|
-
require_relative 'upgrow/immutable_object'
|
8
|
-
require_relative 'upgrow/basic_repository'
|
9
|
-
require_relative 'upgrow/immutable_struct'
|
10
|
-
require_relative 'upgrow/repository'
|
4
|
+
require_relative 'upgrow/actions'
|
11
5
|
require_relative 'upgrow/input'
|
12
6
|
require_relative 'upgrow/model'
|
13
|
-
require_relative 'upgrow/
|
7
|
+
require_relative 'upgrow/repository'
|
14
8
|
|
15
9
|
# The gem's main namespace.
|
16
10
|
module Upgrow
|
data/lib/upgrow/action.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'result'
|
4
|
+
|
3
5
|
module Upgrow
|
4
6
|
# Actions represent the entry points to the app’s core logic. These objects
|
5
7
|
# coordinate workflows in order to get operations and activities done.
|
@@ -23,32 +25,80 @@ module Upgrow
|
|
23
25
|
# own set of required arguments for perform, as well what can be expected as
|
24
26
|
# the result of that method.
|
25
27
|
class Action
|
28
|
+
# Module to be prepended to subclasses of Action. This allows Action to
|
29
|
+
# decorate methods implemented by subclasses so they can have additional
|
30
|
+
# behaviour.
|
31
|
+
module Decorator
|
32
|
+
def perform(...)
|
33
|
+
catch(:failure) do
|
34
|
+
super
|
35
|
+
result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
26
40
|
class << self
|
27
|
-
attr_writer :
|
41
|
+
attr_writer :exposures
|
28
42
|
|
29
|
-
# Each Action class has its own
|
30
|
-
#
|
43
|
+
# Each Action class has its own list of exposed instance variables to be
|
44
|
+
# included in the returned Result when the Action is called.
|
31
45
|
#
|
32
|
-
# @return [
|
33
|
-
|
34
|
-
|
35
|
-
@result_class ||= Result.new
|
46
|
+
# @return [Array] the list of exposed instance variables.
|
47
|
+
def exposures
|
48
|
+
@exposures ||= []
|
36
49
|
end
|
37
50
|
|
38
|
-
# Sets the
|
51
|
+
# Sets the given instance variable names as exposed in the Result.
|
39
52
|
#
|
40
|
-
# @param
|
41
|
-
|
42
|
-
|
53
|
+
# @param names [Array<Symbol>] the list of instance variable names to be
|
54
|
+
# exposed as part of the Result.
|
55
|
+
def expose(*names)
|
56
|
+
@exposures += names
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def inherited(subclass)
|
62
|
+
super
|
63
|
+
subclass.exposures = exposures.dup
|
64
|
+
subclass.prepend(Decorator)
|
43
65
|
end
|
44
66
|
end
|
45
67
|
|
46
|
-
#
|
47
|
-
# to the Action class's method (see #result_class).
|
68
|
+
# Throws a Result populated with the given errors.
|
48
69
|
#
|
49
|
-
# @
|
50
|
-
|
51
|
-
|
70
|
+
# @param errors [Array<Error>, ActiveModel::Errors] the errors object to
|
71
|
+
# be set as the Result errors. If an ActiveModel::Errors object is
|
72
|
+
# provided, it will be converted to an Array of Errors.
|
73
|
+
#
|
74
|
+
# @return [:failure, Result] the Result instance populated with the given
|
75
|
+
# errors.
|
76
|
+
def failure(*errors)
|
77
|
+
errors = errors.first if errors.first.respond_to?(:each)
|
78
|
+
|
79
|
+
if errors.respond_to?(:full_message)
|
80
|
+
errors = errors.map do |error|
|
81
|
+
Error.new(
|
82
|
+
attribute: error.attribute,
|
83
|
+
code: error.type,
|
84
|
+
message: error.full_message
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
throw(:failure, result(errors: errors))
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def result(members = {})
|
95
|
+
result_class = Result.new(*self.class.exposures)
|
96
|
+
|
97
|
+
values = self.class.exposures.to_h do |name|
|
98
|
+
[name, instance_variable_get("@#{name}")]
|
99
|
+
end
|
100
|
+
|
101
|
+
result_class.new(**values.merge(members))
|
52
102
|
end
|
53
103
|
end
|
54
104
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Upgrow
|
4
|
+
# This module offers helpers to work with the collection of Actions loaded in
|
5
|
+
# the application.
|
6
|
+
#
|
7
|
+
# Actions are created with a concrete, predefined interface. This allows them
|
8
|
+
# to be uniformly instantiated in the context of an application, which is
|
9
|
+
# helpful to prevent duplications.
|
10
|
+
module Actions
|
11
|
+
extend self
|
12
|
+
|
13
|
+
# Convenience method to retrieve an Action based on a key name optionally
|
14
|
+
# namespaced.
|
15
|
+
#
|
16
|
+
# @example Retrieve Action based on a global key name
|
17
|
+
# Actions['show'] #=> ShowAction
|
18
|
+
#
|
19
|
+
# @example Retrieve a namespaced Action
|
20
|
+
# Actions['articles', 'show'] #=> Articles::ShowAction
|
21
|
+
#
|
22
|
+
# @param names [Array<String>] one or more names, the last one being the
|
23
|
+
# name of the Action without the "Action" suffix, optionally lowercased.
|
24
|
+
#
|
25
|
+
# @return [Action] the Action specified by the names.
|
26
|
+
def [](*names)
|
27
|
+
action_class_name = names.map(&:capitalize).join('::') + 'Action'
|
28
|
+
Object.const_get(action_class_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -5,6 +5,30 @@ module Upgrow
|
|
5
5
|
# included in a Repository class, it sets the default base to be a class
|
6
6
|
# ending with `Record`.
|
7
7
|
module ActiveRecordAdapter
|
8
|
+
# Class methods for classes that include this module.
|
9
|
+
module ClassMethods
|
10
|
+
# Callback method used by Basic Repository to set a default Repository
|
11
|
+
# base when one is not explicitly provided at the Repository
|
12
|
+
# initialization.
|
13
|
+
#
|
14
|
+
# It attempts to find a constant based on the Repository name, with the
|
15
|
+
# `Record` suffix as a convention. For example, a `UserRepository` would
|
16
|
+
# have the `UserRecord` as its base. That is the naming convention for
|
17
|
+
# Active Record classes under this architecture.
|
18
|
+
#
|
19
|
+
# @return [Class] the Active Record Base class to be used as the
|
20
|
+
# Repository base according to the architecture's naming convention.
|
21
|
+
def default_base
|
22
|
+
base_name = name[/\A(.+)Repository\z/, 1] + 'Record'
|
23
|
+
Object.const_get(base_name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# @private
|
28
|
+
def self.included(base)
|
29
|
+
base.extend(ClassMethods)
|
30
|
+
end
|
31
|
+
|
8
32
|
# Fetches all Records and returns them as an Array of Models.
|
9
33
|
#
|
10
34
|
# @return [Array<Model>] a collection of Models representing all persisted
|
@@ -54,20 +78,5 @@ module Upgrow
|
|
54
78
|
def delete(id)
|
55
79
|
base.destroy(id)
|
56
80
|
end
|
57
|
-
|
58
|
-
# Callback method used by Basic Repository to set a default Repository base
|
59
|
-
# when one is not explicitly provided at the Repository initialization.
|
60
|
-
#
|
61
|
-
# It attempts to find a constant based on the Repository name, with the
|
62
|
-
# `Record` suffix as a convention. For example, a `UserRepository` would
|
63
|
-
# have the `UserRecord` as its base. That is the naming convention for
|
64
|
-
# Active Record classes under this architecture.
|
65
|
-
#
|
66
|
-
# @return [Class] the Active Record Base class to be used as the Repository
|
67
|
-
# base according to the architecture's naming convention.
|
68
|
-
def default_base
|
69
|
-
base_name = self.class.name[/\A(.+)Repository\z/, 1] + 'Record'
|
70
|
-
Object.const_get(base_name)
|
71
|
-
end
|
72
81
|
end
|
73
82
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Upgrow
|
4
|
+
# A Schema that dynamically infers attribute names from an Active Record Base,
|
5
|
+
# plus the attributes from an original Schema.
|
6
|
+
#
|
7
|
+
# This object is used to power Models so they can include Active Record
|
8
|
+
# attributes from a matching class based on the Model's name.
|
9
|
+
class ActiveRecordSchema
|
10
|
+
# Set the Schema's initial state.
|
11
|
+
#
|
12
|
+
# @param base_name [String] the name of the Active Record Base class that
|
13
|
+
# should be used to fetch attribute names.
|
14
|
+
# @param default_schema [ModelSchema] the original Model Schema to be used
|
15
|
+
# to fetch and define custom attributes.
|
16
|
+
def initialize(base_name, default_schema)
|
17
|
+
@base_name = base_name
|
18
|
+
@default_schema = default_schema
|
19
|
+
end
|
20
|
+
|
21
|
+
# Define a custom attribute in the default Schema.
|
22
|
+
#
|
23
|
+
# @param name [Symbol] the name of the new attribute.
|
24
|
+
def attribute(name)
|
25
|
+
@default_schema.attribute(name)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Define a custom association in the default Schema.
|
29
|
+
#
|
30
|
+
# @param name [Symbol] the name of the association.
|
31
|
+
def association(name)
|
32
|
+
@default_schema.association(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# The list of attribute names. This is an aggregate of both the attributes
|
36
|
+
# from the Active Record Base as well as any custom attributes from the
|
37
|
+
# default Schema.
|
38
|
+
#
|
39
|
+
# @return [Array<Symbol>] the list of attribute names.
|
40
|
+
def attribute_names
|
41
|
+
base.attribute_names.map(&:to_sym) | @default_schema.attribute_names
|
42
|
+
end
|
43
|
+
|
44
|
+
# The list of association names. This is an aggregate of both the
|
45
|
+
# associations from the Active Record Base as well as any custom
|
46
|
+
# associations from the default Schema.
|
47
|
+
#
|
48
|
+
# @return [Array<Symbol>] the list of attribute names.
|
49
|
+
def association_names
|
50
|
+
association_names = base.reflections.keys.map do |key|
|
51
|
+
key.sub('_record', '').to_sym
|
52
|
+
end
|
53
|
+
|
54
|
+
association_names | @default_schema.association_names
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def base
|
60
|
+
Object.const_get(@base_name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'immutable_object'
|
4
|
+
require_relative 'model_schema'
|
5
|
+
|
6
|
+
module Upgrow
|
7
|
+
# Base class for Models. As an Immutable Object, it sets a default schema with
|
8
|
+
# the minimal attribute ID, as well as requires all attributes to be set upon
|
9
|
+
# initialization.
|
10
|
+
class BasicModel < ImmutableObject
|
11
|
+
class AssociationNotLoadedError < StandardError; end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# Defines an association in the Model Schema.
|
15
|
+
#
|
16
|
+
# @param name [Symbol] the name of the association.
|
17
|
+
def belongs_to(name)
|
18
|
+
schema.association(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Defines an association in the Model Schema.
|
22
|
+
#
|
23
|
+
# @param name [Symbol] the name of the association.
|
24
|
+
def has_many(name)
|
25
|
+
schema.association(name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
self.schema = ModelSchema.new
|
30
|
+
|
31
|
+
attribute :id
|
32
|
+
|
33
|
+
attr_reader :associations
|
34
|
+
|
35
|
+
# Initializes a new Model with the given member values.
|
36
|
+
#
|
37
|
+
# @param args [Hash<Symbol, Object>] the list of values for each attribute
|
38
|
+
# and association.
|
39
|
+
#
|
40
|
+
# @raise [KeyError] if an attribute is missing in the list of arguments.
|
41
|
+
def initialize(**args)
|
42
|
+
@associations = self.class.schema.association_names.to_h do |name|
|
43
|
+
[name, args[name]]
|
44
|
+
end.freeze
|
45
|
+
|
46
|
+
attributes = self.class.schema.attribute_names.to_h do |name|
|
47
|
+
[name, args.fetch(name)]
|
48
|
+
end
|
49
|
+
|
50
|
+
super(**attributes)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def method_missing(name, *args, &block)
|
56
|
+
return super unless associations.include?(name)
|
57
|
+
associations[name] || raise(AssociationNotLoadedError)
|
58
|
+
end
|
59
|
+
|
60
|
+
def respond_to_missing?(name, _include_private = false)
|
61
|
+
associations.include?(name) || super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -5,41 +5,68 @@ module Upgrow
|
|
5
5
|
# Repositories should have, as well as the logic on how to materialize data
|
6
6
|
# into Models.
|
7
7
|
class BasicRepository
|
8
|
+
class << self
|
9
|
+
attr_writer :base
|
10
|
+
attr_writer :model_class
|
11
|
+
|
12
|
+
# model_class [Class] the Model class to be used to map and return the
|
13
|
+
# materialized data as instances of the domain. Defaults to a constant
|
14
|
+
# derived from the Repository class' name. For example, a `UserRepository`
|
15
|
+
# will have its default Model class set to `User`.
|
16
|
+
#
|
17
|
+
# @return [Class] the Repository Model class.
|
18
|
+
def model_class
|
19
|
+
@model_class || default_model_class
|
20
|
+
end
|
21
|
+
|
22
|
+
# the base object to be used internally to retrieve the persisted data.
|
23
|
+
# For example, a base class in which queries can be performed for a
|
24
|
+
# relational database adapter. Defaults to `nil`.
|
25
|
+
#
|
26
|
+
# @return [Object] the Repository base.
|
27
|
+
def base
|
28
|
+
@base || default_base
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def default_base; end
|
34
|
+
|
35
|
+
def default_model_class
|
36
|
+
model_class_name = name.delete_suffix('Repository')
|
37
|
+
Object.const_get(model_class_name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
8
41
|
attr_reader :base, :model_class
|
9
42
|
|
10
43
|
# Sets the Basic Repositorie's state.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# performed for a relational database adapter. Defaults to `nil`.
|
15
|
-
#
|
16
|
-
# @param model_class [Class] the Model class to be used to map and return
|
17
|
-
# the materialized data as instances of the domain. Defaults to a constant
|
18
|
-
# derived from the Repository class' name. For example, a `UserRepository`
|
19
|
-
# will have its default Model class set to `User`.
|
20
|
-
def initialize(base: default_base, model_class: default_model_class)
|
21
|
-
@base = base
|
22
|
-
@model_class = model_class
|
44
|
+
def initialize
|
45
|
+
@base = self.class.base
|
46
|
+
@model_class = self.class.model_class
|
23
47
|
end
|
24
48
|
|
25
49
|
# Represents the raw Hash of data attributes as a Model instance from the
|
26
50
|
# Repositorie's Model class.
|
27
51
|
#
|
52
|
+
# @param model_class_or_attributes [Class, Hash<Symbol, Object>] the Model
|
53
|
+
# class to be instantiated, in case it is a different class than the
|
54
|
+
# Repositorie's Model class, or the list of attributes the model will
|
55
|
+
# have, in case the Model class is the Repositorie's Model class.
|
28
56
|
# @param attributes [Hash<Symbol, Object>] the list of attributes the Model
|
29
|
-
# will have
|
57
|
+
# will have, in case the Model to be instantiated is passed as the first
|
58
|
+
# argument.
|
30
59
|
#
|
31
60
|
# @return [Model] the Model instance populated with the given attributes.
|
32
|
-
def to_model(attributes = {})
|
33
|
-
model_class
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
61
|
+
def to_model(model_class_or_attributes, attributes = {})
|
62
|
+
model_class = model_class_or_attributes
|
37
63
|
|
38
|
-
|
64
|
+
if model_class_or_attributes.respond_to?(:transform_keys)
|
65
|
+
model_class = self.model_class
|
66
|
+
attributes = model_class_or_attributes
|
67
|
+
end
|
39
68
|
|
40
|
-
|
41
|
-
model_class_name = self.class.name[/\A(.+)Repository\z/, 1]
|
42
|
-
Object.const_get(model_class_name)
|
69
|
+
model_class.new(**attributes.transform_keys(&:to_sym))
|
43
70
|
end
|
44
71
|
end
|
45
72
|
end
|