upgrow 0.0.3 → 0.0.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 +4 -4
- data/lib/upgrow/action.rb +9 -4
- data/lib/upgrow/actions.rb +1 -1
- data/lib/upgrow/active_record_conversion.rb +43 -0
- data/lib/upgrow/{active_record_adapter.rb → active_record_queries.rb} +6 -6
- data/lib/upgrow/basic_model.rb +5 -1
- data/lib/upgrow/basic_repository.rb +1 -41
- data/lib/upgrow/immutable_struct.rb +1 -0
- data/lib/upgrow/input.rb +2 -3
- data/lib/upgrow/model.rb +3 -3
- data/lib/upgrow/repository.rb +5 -3
- data/lib/upgrow/result.rb +1 -1
- data/test/dummy/app/actions/application_action.rb +1 -1
- data/test/dummy/app/actions/articles/new_action.rb +1 -2
- data/test/dummy/app/actions/comments/new_action.rb +1 -2
- data/test/dummy/app/actions/sessions/new_action.rb +1 -2
- data/test/dummy/app/actions/users/new_action.rb +1 -2
- data/test/dummy/app/channels/application_cable/channel.rb +1 -0
- data/test/dummy/app/channels/application_cable/connection.rb +1 -0
- data/test/dummy/app/controllers/application_controller.rb +1 -0
- data/test/dummy/app/helpers/application_helper.rb +3 -4
- data/test/dummy/app/jobs/application_job.rb +1 -0
- data/test/dummy/app/mailers/application_mailer.rb +1 -0
- data/test/dummy/app/records/user_record.rb +1 -0
- data/test/dummy/app/repositories/article_repository.rb +7 -16
- data/test/dummy/app/repositories/comment_repository.rb +1 -0
- data/test/dummy/app/repositories/user_repository.rb +3 -2
- data/test/dummy/config/application.rb +1 -0
- data/test/dummy/config/boot.rb +1 -0
- data/test/dummy/config/environment.rb +1 -0
- data/test/dummy/config/environments/development.rb +1 -0
- data/test/dummy/config/environments/production.rb +2 -1
- data/test/dummy/config/environments/test.rb +1 -0
- data/test/dummy/config/initializers/assets.rb +1 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +1 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +1 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +1 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +1 -0
- data/test/dummy/config/puma.rb +5 -4
- data/test/dummy/db/migrate/20210219211631_create_articles.rb +1 -0
- data/test/dummy/db/migrate/20210320140432_create_comments.rb +1 -0
- data/test/dummy/db/migrate/20210409164927_create_users.rb +1 -0
- data/test/rails_helper.rb +1 -1
- data/test/system/articles_test.rb +2 -2
- data/test/test_helper.rb +38 -0
- data/test/upgrow/action_test.rb +2 -2
- data/test/upgrow/active_record_conversion_test.rb +85 -0
- data/test/upgrow/{active_record_adapter_test.rb → active_record_queries_test.rb} +10 -12
- data/test/upgrow/basic_model_test.rb +6 -3
- data/test/upgrow/basic_repository_test.rb +0 -62
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee034f6fa4ddd317a01b6f80d4762259f205a6763cd06fdfa637cdac186f0063
|
4
|
+
data.tar.gz: 82b9fa66aab3ca2b3bec78ef2c04415d5aa199c0eb9734216010ae0806bb5476
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 720d3134774aac67239a7681e2e61dcc525fbd8e38852ebb07f2ec82b8be8cc61ac0b71bcaea13fd375e2fc027bd5092aae11fdd70269da1d1fb299325afbd42
|
7
|
+
data.tar.gz: 0f16e74a9a07c2e186f10ec14806fc8c59890149204252f25ff2fbc083d8284625f1967a9a74db5e5d1dfb377f315aec864c739a39c8542071a622991bcf8bfb
|
data/lib/upgrow/action.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
require_relative 'result'
|
4
4
|
|
5
5
|
module Upgrow
|
6
|
-
# Actions represent the entry points to the app
|
6
|
+
# Actions represent the entry points to the app's core logic. These objects
|
7
7
|
# coordinate workflows in order to get operations and activities done.
|
8
|
-
# Ultimately, Actions are the public interface of the app
|
8
|
+
# Ultimately, Actions are the public interface of the app's business layers.
|
9
9
|
#
|
10
|
-
# Rails controllers talk to the app
|
10
|
+
# Rails controllers talk to the app's internals by sending messages to
|
11
11
|
# specific Actions, optionally with the required inputs. Actions have a
|
12
12
|
# one-to-one relationship with incoming requests: they are paired
|
13
13
|
# symmetrically with end-user intents and demands. This is quite a special
|
@@ -15,7 +15,7 @@ module Upgrow
|
|
15
15
|
# should be handled by a single Action.
|
16
16
|
#
|
17
17
|
# The fact that each Action represents a meaningful and complete
|
18
|
-
# request-response cycle forces modularization for the app
|
18
|
+
# request-response cycle forces modularization for the app's business logic,
|
19
19
|
# exposing immediately complex relationships between objects at the same time
|
20
20
|
# that frees up the app from scenarios such as interdependent requests. In
|
21
21
|
# other words, Actions do not have knowledge or coupling between other Actions
|
@@ -29,6 +29,11 @@ module Upgrow
|
|
29
29
|
# decorate methods implemented by subclasses so they can have additional
|
30
30
|
# behaviour.
|
31
31
|
module Decorator
|
32
|
+
# Calls the original `perform` method of the Action object and returns its
|
33
|
+
# Result. In case the Action throws a `:failure`, catches and returns its
|
34
|
+
# value, which is supposed to be a failed Result generated by the Action.
|
35
|
+
#
|
36
|
+
# @return [Result] the Action Result.
|
32
37
|
def perform(...)
|
33
38
|
catch(:failure) do
|
34
39
|
super
|
data/lib/upgrow/actions.rb
CHANGED
@@ -24,7 +24,7 @@ module Upgrow
|
|
24
24
|
#
|
25
25
|
# @return [Action] the Action specified by the names.
|
26
26
|
def [](*names)
|
27
|
-
action_class_name = names.map(&:capitalize).join(
|
27
|
+
action_class_name = "#{names.map(&:capitalize).join("::")}Action"
|
28
28
|
Object.const_get(action_class_name)
|
29
29
|
end
|
30
30
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Upgrow
|
4
|
+
# Offers convenience funcionality to convert Active Records into Models.
|
5
|
+
module ActiveRecordConversion
|
6
|
+
# Converts the given Active Record or Records into Models.
|
7
|
+
#
|
8
|
+
# @param record_or_enum [ActiveRecord::Base, Enumerable] an Active Record
|
9
|
+
# instance or a collection of Records to be converted.
|
10
|
+
#
|
11
|
+
# @return [Model] the Model instance generated based on the single Record
|
12
|
+
# given.
|
13
|
+
# @return [Array<Model>] the collection of Model instances generated based
|
14
|
+
# on the collection of Records provided.
|
15
|
+
# @return [nil] if the given value is nil.
|
16
|
+
def to_model(record_or_enum)
|
17
|
+
if record_or_enum.respond_to?(:map)
|
18
|
+
record_or_enum.map do |record|
|
19
|
+
_to_model(record)
|
20
|
+
end
|
21
|
+
elsif !record_or_enum.nil?
|
22
|
+
_to_model(record_or_enum)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @private
|
27
|
+
def _to_model(record)
|
28
|
+
associations = record.class.reflections.keys.map do |reflection|
|
29
|
+
association = record.association(reflection.to_sym)
|
30
|
+
next unless association.loaded?
|
31
|
+
|
32
|
+
name = reflection.sub('_record', '').to_sym
|
33
|
+
[name, to_model(record.public_send(reflection))]
|
34
|
+
end.compact.to_h
|
35
|
+
|
36
|
+
model_class = Object.const_get(record.class.name.delete_suffix('Record'))
|
37
|
+
|
38
|
+
attributes = record.attributes.merge(associations)
|
39
|
+
|
40
|
+
model_class.new(**attributes.transform_keys(&:to_sym))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -4,7 +4,7 @@ module Upgrow
|
|
4
4
|
# Mixin that implements Repository methods with an Active Record Base. When
|
5
5
|
# included in a Repository class, it sets the default base to be a class
|
6
6
|
# ending with `Record`.
|
7
|
-
module
|
7
|
+
module ActiveRecordQueries
|
8
8
|
# Class methods for classes that include this module.
|
9
9
|
module ClassMethods
|
10
10
|
# Callback method used by Basic Repository to set a default Repository
|
@@ -19,7 +19,7 @@ module Upgrow
|
|
19
19
|
# @return [Class] the Active Record Base class to be used as the
|
20
20
|
# Repository base according to the architecture's naming convention.
|
21
21
|
def default_base
|
22
|
-
base_name = name[/\A(.+)Repository\z/, 1]
|
22
|
+
base_name = "#{name[/\A(.+)Repository\z/, 1]}Record"
|
23
23
|
Object.const_get(base_name)
|
24
24
|
end
|
25
25
|
end
|
@@ -34,7 +34,7 @@ module Upgrow
|
|
34
34
|
# @return [Array<Model>] a collection of Models representing all persisted
|
35
35
|
# Records.
|
36
36
|
def all
|
37
|
-
base.all
|
37
|
+
to_model(base.all)
|
38
38
|
end
|
39
39
|
|
40
40
|
# Persists a new Record with the given input, and materializes the newly
|
@@ -46,7 +46,7 @@ module Upgrow
|
|
46
46
|
# Record.
|
47
47
|
def create(input)
|
48
48
|
record = base.create!(input.attributes)
|
49
|
-
to_model(record
|
49
|
+
to_model(record)
|
50
50
|
end
|
51
51
|
|
52
52
|
# Retrieves the Record with the given ID, representing its data as a Model.
|
@@ -57,7 +57,7 @@ module Upgrow
|
|
57
57
|
# ID.
|
58
58
|
def find(id)
|
59
59
|
record = base.find(id)
|
60
|
-
to_model(record
|
60
|
+
to_model(record)
|
61
61
|
end
|
62
62
|
|
63
63
|
# Updates the Record with the given ID with the given Input attributes.
|
@@ -69,7 +69,7 @@ module Upgrow
|
|
69
69
|
# @return [Model] the Model instance with the updated data of the Record.
|
70
70
|
def update(id, input)
|
71
71
|
record = base.update(id, input.attributes)
|
72
|
-
to_model(record
|
72
|
+
to_model(record)
|
73
73
|
end
|
74
74
|
|
75
75
|
# Deletes the Record that has the given ID.
|
data/lib/upgrow/basic_model.rb
CHANGED
@@ -54,7 +54,11 @@ module Upgrow
|
|
54
54
|
|
55
55
|
def method_missing(name, *args, &block)
|
56
56
|
return super unless associations.include?(name)
|
57
|
-
|
57
|
+
|
58
|
+
associations[name] || raise(
|
59
|
+
AssociationNotLoadedError,
|
60
|
+
"Association #{name} not loaded for #{self.class.name}."
|
61
|
+
)
|
58
62
|
end
|
59
63
|
|
60
64
|
def respond_to_missing?(name, _include_private = false)
|
@@ -7,17 +7,6 @@ module Upgrow
|
|
7
7
|
class BasicRepository
|
8
8
|
class << self
|
9
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
10
|
|
22
11
|
# the base object to be used internally to retrieve the persisted data.
|
23
12
|
# For example, a base class in which queries can be performed for a
|
@@ -31,42 +20,13 @@ module Upgrow
|
|
31
20
|
private
|
32
21
|
|
33
22
|
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
23
|
end
|
40
24
|
|
41
|
-
attr_reader :base
|
25
|
+
attr_reader :base
|
42
26
|
|
43
27
|
# Sets the Basic Repositorie's state.
|
44
28
|
def initialize
|
45
29
|
@base = self.class.base
|
46
|
-
@model_class = self.class.model_class
|
47
|
-
end
|
48
|
-
|
49
|
-
# Represents the raw Hash of data attributes as a Model instance from the
|
50
|
-
# Repositorie's Model class.
|
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.
|
56
|
-
# @param attributes [Hash<Symbol, Object>] the list of attributes the Model
|
57
|
-
# will have, in case the Model to be instantiated is passed as the first
|
58
|
-
# argument.
|
59
|
-
#
|
60
|
-
# @return [Model] the Model instance populated with the given attributes.
|
61
|
-
def to_model(model_class_or_attributes, attributes = {})
|
62
|
-
model_class = model_class_or_attributes
|
63
|
-
|
64
|
-
if model_class_or_attributes.respond_to?(:transform_keys)
|
65
|
-
model_class = self.model_class
|
66
|
-
attributes = model_class_or_attributes
|
67
|
-
end
|
68
|
-
|
69
|
-
model_class.new(**attributes.transform_keys(&:to_sym))
|
70
30
|
end
|
71
31
|
end
|
72
32
|
end
|
data/lib/upgrow/input.rb
CHANGED
@@ -21,7 +21,7 @@ module Upgrow
|
|
21
21
|
# integrity for inputs, since user-entered data can contain any information of
|
22
22
|
# different types, or even not to be present at all.
|
23
23
|
#
|
24
|
-
# User input validation is a core part of any app
|
24
|
+
# User input validation is a core part of any app's business logic. It ensures
|
25
25
|
# that incoming data is sane, proper, and respects a predefined schema. A
|
26
26
|
# default Rails app overloads Record objects with yet another responsibility:
|
27
27
|
# being the place where validation rules are written and checked. While there
|
@@ -48,7 +48,6 @@ module Upgrow
|
|
48
48
|
|
49
49
|
# Overwrites the validation context writer so the Input's state is not
|
50
50
|
# mutated.
|
51
|
-
def validation_context=(_)
|
52
|
-
end
|
51
|
+
def validation_context=(_); end
|
53
52
|
end
|
54
53
|
end
|
data/lib/upgrow/model.rb
CHANGED
@@ -4,7 +4,7 @@ require_relative 'active_record_schema'
|
|
4
4
|
require_relative 'basic_model'
|
5
5
|
|
6
6
|
module Upgrow
|
7
|
-
# Models are objects that represent core entities of the app
|
7
|
+
# Models are objects that represent core entities of the app's business logic.
|
8
8
|
# These are usually persisted and can be fetched and created as needed. They
|
9
9
|
# have unique keys for identification (usually a numeric value), and, most
|
10
10
|
# importantly perhaps, they are immutable. This is the key difference between
|
@@ -12,7 +12,7 @@ module Upgrow
|
|
12
12
|
# referred to as models in typical Rails default apps.
|
13
13
|
#
|
14
14
|
# Another difference between Models and Records is that, once instantiated,
|
15
|
-
# Models simply hold its attributes immutably, and they don
|
15
|
+
# Models simply hold its attributes immutably, and they don't have any
|
16
16
|
# capabilities to create or update any information in the persistence layer.
|
17
17
|
#
|
18
18
|
# The collaboration between Repositories and Models is what allows Active
|
@@ -27,7 +27,7 @@ module Upgrow
|
|
27
27
|
super
|
28
28
|
|
29
29
|
subclass.schema = ActiveRecordSchema.new(
|
30
|
-
subclass.name
|
30
|
+
"#{subclass.name}Record", subclass.schema
|
31
31
|
)
|
32
32
|
end
|
33
33
|
end
|
data/lib/upgrow/repository.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'active_record_conversion'
|
4
|
+
require_relative 'active_record_queries'
|
4
5
|
require_relative 'basic_repository'
|
5
6
|
|
6
7
|
module Upgrow
|
7
8
|
# Repositories are responsible for the persistence layer of the app. They
|
8
|
-
# encapsulate Rails
|
9
|
+
# encapsulate Rails' Active Record in a subset of simple methods for querying
|
9
10
|
# and persistence of data, and return simple read-only objects as a result.
|
10
11
|
# This allows the app to isolate Active Record only to this subset, exposing
|
11
12
|
# only the desired queries and methods to other layers through Repositories.
|
12
13
|
class Repository < BasicRepository
|
13
|
-
include
|
14
|
+
include ActiveRecordConversion
|
15
|
+
include ActiveRecordQueries
|
14
16
|
end
|
15
17
|
end
|
data/lib/upgrow/result.rb
CHANGED
@@ -18,7 +18,7 @@ module Upgrow
|
|
18
18
|
#
|
19
19
|
# Additionally, Result instances behave like monadic values by offering
|
20
20
|
# bindings to be called only in case of success or failure, which further
|
21
|
-
# simplifies the caller
|
21
|
+
# simplifies the caller's code by not having to use conditional to check for
|
22
22
|
# errors.
|
23
23
|
class Result < ImmutableStruct
|
24
24
|
class << self
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module ApplicationHelper
|
3
4
|
class BulmaFormBuilder < ActionView::Helpers::FormBuilder
|
4
5
|
# Generic field wrapper for form inputs.
|
@@ -111,12 +112,10 @@ module ApplicationHelper
|
|
111
112
|
|
112
113
|
private
|
113
114
|
|
114
|
-
def control(expanded: false)
|
115
|
+
def control(expanded: false, &block)
|
115
116
|
control_class = ['control']
|
116
117
|
control_class << ['is-expanded'] if expanded
|
117
|
-
@template.content_tag(:p, class: control_class)
|
118
|
-
yield
|
119
|
-
end
|
118
|
+
@template.content_tag(:p, class: control_class, &block)
|
120
119
|
end
|
121
120
|
|
122
121
|
def errors_for(method)
|
@@ -2,29 +2,20 @@
|
|
2
2
|
|
3
3
|
class ArticleRepository < Upgrow::Repository
|
4
4
|
def all
|
5
|
-
base.all.includes(:user_record)
|
6
|
-
user = to_model(User, record.user_record.attributes)
|
7
|
-
to_model(record.attributes.merge(user: user))
|
8
|
-
end
|
5
|
+
to_model(base.all.includes(:user_record))
|
9
6
|
end
|
10
7
|
|
11
8
|
def find_with_comments(id)
|
12
|
-
record =
|
9
|
+
record = base
|
10
|
+
.includes(:user_record)
|
11
|
+
.includes(comment_records: :user_record)
|
12
|
+
.find(id)
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
comments = comment_records.map do |comment_record|
|
17
|
-
user = to_model(User, comment_record.user_record.attributes)
|
18
|
-
to_model(Comment, comment_record.attributes.merge(user: user))
|
19
|
-
end
|
20
|
-
|
21
|
-
user = to_model(User, record.user_record.attributes)
|
22
|
-
|
23
|
-
to_model(record.attributes.merge(comments: comments, user: user))
|
14
|
+
to_model(record)
|
24
15
|
end
|
25
16
|
|
26
17
|
def find_for_user(id, user:)
|
27
18
|
record = base.find_by(id: id, user_id: user.id)
|
28
|
-
to_model(record
|
19
|
+
to_model(record)
|
29
20
|
end
|
30
21
|
end
|
@@ -3,10 +3,11 @@
|
|
3
3
|
class UserRepository < Upgrow::Repository
|
4
4
|
def find_for_authentication(input)
|
5
5
|
record = UserRecord.find_by(email: input.email)
|
6
|
-
to_model(record
|
6
|
+
to_model(record) if record&.authenticate(input.password)
|
7
7
|
end
|
8
8
|
|
9
9
|
def find_from_context(id)
|
10
|
-
UserRecord.find_by(id: id)
|
10
|
+
record = UserRecord.find_by(id: id)
|
11
|
+
to_model(record)
|
11
12
|
end
|
12
13
|
end
|
data/test/dummy/config/boot.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'active_support/core_ext/integer/time'
|
3
4
|
|
4
5
|
Rails.application.configure do
|
@@ -100,7 +101,7 @@ Rails.application.configure do
|
|
100
101
|
# )
|
101
102
|
|
102
103
|
if ENV['RAILS_LOG_TO_STDOUT'].present?
|
103
|
-
logger = ActiveSupport::Logger.new(
|
104
|
+
logger = ActiveSupport::Logger.new($stdout)
|
104
105
|
logger.formatter = config.log_formatter
|
105
106
|
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
106
107
|
end
|
data/test/dummy/config/puma.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
# Puma can serve each request in a thread from an internal thread pool.
|
3
4
|
# The `threads` method setting takes two numbers: a minimum and maximum.
|
4
5
|
# Any libraries that use thread pools should be configured to match
|
5
6
|
# the maximum value specified for Puma. Default is set to 5 threads for minimum
|
6
7
|
# and maximum; this matches the default thread size of Active Record.
|
7
8
|
#
|
8
|
-
max_threads_count = ENV.fetch('RAILS_MAX_THREADS'
|
9
|
+
max_threads_count = ENV.fetch('RAILS_MAX_THREADS', 5)
|
9
10
|
min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count }
|
10
11
|
threads min_threads_count, max_threads_count
|
11
12
|
|
@@ -16,14 +17,14 @@ worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development'
|
|
16
17
|
|
17
18
|
# Specifies the `port` that Puma will listen on to receive requests.
|
18
19
|
#
|
19
|
-
port ENV.fetch('PORT'
|
20
|
+
port ENV.fetch('PORT', 3000)
|
20
21
|
|
21
22
|
# Specifies the `environment` that Puma will run in.
|
22
23
|
#
|
23
|
-
environment ENV.fetch('RAILS_ENV'
|
24
|
+
environment ENV.fetch('RAILS_ENV', 'development')
|
24
25
|
|
25
26
|
# Specifies the `pidfile` that Puma will use.
|
26
|
-
pidfile ENV.fetch('PIDFILE'
|
27
|
+
pidfile ENV.fetch('PIDFILE', 'tmp/pids/server.pid')
|
27
28
|
|
28
29
|
# Specifies the number of `workers` to boot in clustered mode.
|
29
30
|
# Workers are forked web server processes. If using threads and workers together
|
data/test/rails_helper.rb
CHANGED
@@ -14,7 +14,7 @@ if ActiveSupport::TestCase.respond_to?(:fixture_path=)
|
|
14
14
|
ActionDispatch::IntegrationTest.fixture_path =
|
15
15
|
ActiveSupport::TestCase.fixture_path
|
16
16
|
ActiveSupport::TestCase.file_fixture_path =
|
17
|
-
ActiveSupport::TestCase.fixture_path
|
17
|
+
"#{ActiveSupport::TestCase.fixture_path}/files"
|
18
18
|
ActiveSupport::TestCase.fixtures(:all)
|
19
19
|
end
|
20
20
|
|
@@ -120,7 +120,7 @@ class ArticlesTest < ApplicationSystemTestCase
|
|
120
120
|
test 'guest User cannot access the edit page directly' do
|
121
121
|
click_link 'Lorem Barnak'
|
122
122
|
|
123
|
-
visit current_path
|
123
|
+
visit "#{current_path}/edit"
|
124
124
|
|
125
125
|
assert_title 'Sign In'
|
126
126
|
end
|
@@ -137,7 +137,7 @@ class ArticlesTest < ApplicationSystemTestCase
|
|
137
137
|
|
138
138
|
click_link 'The Hobbit'
|
139
139
|
|
140
|
-
visit current_path
|
140
|
+
visit "#{current_path}/edit"
|
141
141
|
|
142
142
|
assert_title 'Sign In'
|
143
143
|
end
|
data/test/test_helper.rb
CHANGED
@@ -10,3 +10,41 @@ module Warning
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
Warning[:deprecated] = true
|
13
|
+
|
14
|
+
class TestRecord
|
15
|
+
class Association
|
16
|
+
def initialize(loaded:)
|
17
|
+
@loaded = loaded
|
18
|
+
end
|
19
|
+
|
20
|
+
def loaded?
|
21
|
+
@loaded
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
attr_accessor :reflections
|
27
|
+
|
28
|
+
def inherited(subclass)
|
29
|
+
super
|
30
|
+
subclass.reflections = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def belongs_to(name)
|
34
|
+
reflections[name.to_s] = :belongs_to
|
35
|
+
define_method(name) { attributes[name.to_s] }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(**attributes)
|
40
|
+
@attributes = attributes
|
41
|
+
end
|
42
|
+
|
43
|
+
def attributes
|
44
|
+
@attributes.transform_keys(&:to_s)
|
45
|
+
end
|
46
|
+
|
47
|
+
def association(name)
|
48
|
+
Association.new(loaded: !public_send(name).nil?)
|
49
|
+
end
|
50
|
+
end
|
data/test/upgrow/action_test.rb
CHANGED
@@ -31,7 +31,7 @@ module Upgrow
|
|
31
31
|
class FailedAction < SampleAction
|
32
32
|
def perform
|
33
33
|
super
|
34
|
-
failure(:
|
34
|
+
failure(:error1, :error2)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -78,7 +78,7 @@ module Upgrow
|
|
78
78
|
result = FailedAction.new.perform
|
79
79
|
|
80
80
|
assert_equal 'volmer', result.user
|
81
|
-
assert_equal [:
|
81
|
+
assert_equal [:error1, :error2], result.errors
|
82
82
|
end
|
83
83
|
|
84
84
|
test '#failure throws a failed Result with the given errors' do
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
require 'upgrow/active_record_conversion'
|
6
|
+
require 'upgrow/basic_model'
|
7
|
+
require 'upgrow/basic_repository'
|
8
|
+
require 'upgrow/input'
|
9
|
+
|
10
|
+
module Upgrow
|
11
|
+
class ActiveRecordConversionTest < ActiveSupport::TestCase
|
12
|
+
class UserRecord < TestRecord
|
13
|
+
end
|
14
|
+
|
15
|
+
class ArticleRecord < TestRecord
|
16
|
+
belongs_to :user_record
|
17
|
+
end
|
18
|
+
|
19
|
+
class User < BasicModel
|
20
|
+
attribute :name
|
21
|
+
end
|
22
|
+
|
23
|
+
class Article < BasicModel
|
24
|
+
attribute :title
|
25
|
+
belongs_to :user
|
26
|
+
end
|
27
|
+
|
28
|
+
class ArticleRepository < BasicRepository
|
29
|
+
include ActiveRecordConversion
|
30
|
+
end
|
31
|
+
|
32
|
+
setup do
|
33
|
+
@repository = ArticleRepository.new
|
34
|
+
@record = ArticleRecord.new(id: 1, title: 'The Hobbit')
|
35
|
+
end
|
36
|
+
|
37
|
+
test '#to_model converts a Record into a Model' do
|
38
|
+
model = @repository.to_model(@record)
|
39
|
+
|
40
|
+
assert_instance_of Article, model
|
41
|
+
assert_equal 1, model.id
|
42
|
+
assert_equal 'The Hobbit', model.title
|
43
|
+
end
|
44
|
+
|
45
|
+
test '#to_model converts an array of Records into an array of Models' do
|
46
|
+
second_record = ArticleRecord.new(id: 2, title: 'Harry Potter')
|
47
|
+
|
48
|
+
models = @repository.to_model([@record, second_record])
|
49
|
+
|
50
|
+
assert_equal 2, models.size
|
51
|
+
|
52
|
+
assert_instance_of Article, models.first
|
53
|
+
assert_equal 'The Hobbit', models.first.title
|
54
|
+
assert_equal 1, models.first.id
|
55
|
+
|
56
|
+
assert_instance_of Article, models.last
|
57
|
+
assert_equal 'Harry Potter', models.last.title
|
58
|
+
assert_equal 2, models.last.id
|
59
|
+
end
|
60
|
+
|
61
|
+
test '#to_model adds any loaded assocation to the resulting Model' do
|
62
|
+
user_record = UserRecord.new(id: 666, name: 'JRR Tolkien')
|
63
|
+
|
64
|
+
record = ArticleRecord.new(
|
65
|
+
id: 1, title: 'The Hobbit', user_record: user_record
|
66
|
+
)
|
67
|
+
|
68
|
+
model = @repository.to_model(record)
|
69
|
+
|
70
|
+
assert_instance_of User, model.user
|
71
|
+
assert_equal 666, model.user.id
|
72
|
+
assert_equal 'JRR Tolkien', model.user.name
|
73
|
+
end
|
74
|
+
|
75
|
+
test '#to_model does not add unloaded assocations to the resulting Model' do
|
76
|
+
model = @repository.to_model(@record)
|
77
|
+
|
78
|
+
assert_raises(BasicModel::AssociationNotLoadedError) { model.user }
|
79
|
+
end
|
80
|
+
|
81
|
+
test '#to_model is nil when the given object is nil' do
|
82
|
+
assert_nil @repository.to_model(nil)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -2,34 +2,32 @@
|
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
4
|
|
5
|
-
require 'upgrow/active_record_adapter'
|
6
5
|
require 'upgrow/basic_model'
|
7
|
-
require 'upgrow/
|
6
|
+
require 'upgrow/repository'
|
8
7
|
require 'upgrow/input'
|
9
8
|
|
10
9
|
module Upgrow
|
11
|
-
class
|
10
|
+
class ActiveRecordQueriesTest < ActiveSupport::TestCase
|
12
11
|
class User < BasicModel
|
13
12
|
attribute :name
|
14
13
|
end
|
15
14
|
|
15
|
+
class UserRecord < TestRecord
|
16
|
+
end
|
17
|
+
|
16
18
|
class UserInput < Input
|
17
19
|
attribute :name
|
18
20
|
end
|
19
21
|
|
20
|
-
class UserRepository <
|
21
|
-
include ActiveRecordAdapter
|
22
|
+
class UserRepository < Repository
|
22
23
|
end
|
23
24
|
|
24
|
-
class UserRecord; end
|
25
|
-
|
26
25
|
setup do
|
27
26
|
@base = Minitest::Mock.new
|
28
27
|
UserRepository.base = @base
|
29
28
|
@repository = UserRepository.new
|
30
|
-
|
31
|
-
@
|
32
|
-
@record.expect(:attributes, @record_attributes)
|
29
|
+
|
30
|
+
@record = UserRecord.new(name: 'volmer', id: 1)
|
33
31
|
end
|
34
32
|
|
35
33
|
test '.base is the Active Record Base class according to the Repository name by default' do
|
@@ -70,9 +68,9 @@ module Upgrow
|
|
70
68
|
|
71
69
|
test '#update changes the existing Record attributes' do
|
72
70
|
input = UserInput.new(name: 'rafael')
|
71
|
+
new_record = UserRecord.new(id: 1, name: 'rafael')
|
73
72
|
|
74
|
-
@base.expect(:update,
|
75
|
-
@record_attributes[:name] = 'rafael'
|
73
|
+
@base.expect(:update, new_record, [1, { name: 'rafael' }])
|
76
74
|
|
77
75
|
model = @repository.update(1, input)
|
78
76
|
|
@@ -76,17 +76,20 @@ module Upgrow
|
|
76
76
|
model = SampleModel.new(
|
77
77
|
title: 'volmer',
|
78
78
|
body: 'My long body',
|
79
|
-
id: 1
|
79
|
+
id: 1
|
80
80
|
)
|
81
81
|
|
82
|
-
assert_raises(Model::AssociationNotLoadedError) { model.user }
|
82
|
+
error = assert_raises(Model::AssociationNotLoadedError) { model.user }
|
83
|
+
message = 'Association user not loaded for ' \
|
84
|
+
'Upgrow::BasicModelTest::SampleModel.'
|
85
|
+
assert_equal message, error.message
|
83
86
|
end
|
84
87
|
|
85
88
|
test 'attribute readers work when association is not loaded' do
|
86
89
|
model = SampleModel.new(
|
87
90
|
title: 'volmer',
|
88
91
|
body: 'My long body',
|
89
|
-
id: 1
|
92
|
+
id: 1
|
90
93
|
)
|
91
94
|
|
92
95
|
assert_equal 'volmer', model.title
|
@@ -4,25 +4,15 @@ require 'test_helper'
|
|
4
4
|
|
5
5
|
module Upgrow
|
6
6
|
class BasicRepositoryTest < ActiveSupport::TestCase
|
7
|
-
class User < BasicModel; end
|
8
|
-
|
9
|
-
class Car < BasicModel
|
10
|
-
attribute :wheels
|
11
|
-
end
|
12
|
-
|
13
7
|
class UserRepository < BasicRepository; end
|
14
8
|
|
15
|
-
class NoModelRepository < BasicRepository; end
|
16
|
-
|
17
9
|
setup do
|
18
10
|
@original_base = UserRepository.base
|
19
|
-
@original_model_class = UserRepository.model_class
|
20
11
|
@repository = UserRepository.new
|
21
12
|
end
|
22
13
|
|
23
14
|
teardown do
|
24
15
|
UserRepository.base = @original_base
|
25
|
-
UserRepository.model_class = @original_model_class
|
26
16
|
end
|
27
17
|
|
28
18
|
test '.base is nil by default' do
|
@@ -34,61 +24,9 @@ module Upgrow
|
|
34
24
|
assert_equal :my_base, UserRepository.base
|
35
25
|
end
|
36
26
|
|
37
|
-
test '.model_class is iferred based on the Repository name' do
|
38
|
-
assert_equal User, UserRepository.model_class
|
39
|
-
end
|
40
|
-
|
41
|
-
test '.model_class can be set' do
|
42
|
-
UserRepository.model_class = :my_model_class
|
43
|
-
assert_equal :my_model_class, UserRepository.model_class
|
44
|
-
end
|
45
|
-
|
46
|
-
test '.model_class raises a Name Error if the Model class is undefined' do
|
47
|
-
error = assert_raises(NameError) do
|
48
|
-
NoModelRepository.model_class
|
49
|
-
end
|
50
|
-
|
51
|
-
assert_includes(
|
52
|
-
error.message,
|
53
|
-
'uninitialized constant Upgrow::BasicRepositoryTest::NoModel'
|
54
|
-
)
|
55
|
-
end
|
56
|
-
|
57
27
|
test '#base is inferred from the Repository class base' do
|
58
28
|
UserRepository.base = :my_base
|
59
29
|
assert_equal :my_base, UserRepository.new.base
|
60
30
|
end
|
61
|
-
|
62
|
-
test '#model_class is inferred from the Repository class Model class' do
|
63
|
-
UserRepository.model_class = :my_model_class
|
64
|
-
assert_equal :my_model_class, UserRepository.new.model_class
|
65
|
-
end
|
66
|
-
|
67
|
-
test '#to_model returns a new Model with the given attributes as keyword arguments' do
|
68
|
-
model = @repository.to_model(id: 1)
|
69
|
-
|
70
|
-
assert_equal 1, model.id
|
71
|
-
end
|
72
|
-
|
73
|
-
test '#to_model accepts a Model class explicitly' do
|
74
|
-
model = @repository.to_model(Car, id: 1, wheels: 4)
|
75
|
-
|
76
|
-
assert_equal 1, model.id
|
77
|
-
assert_equal 4, model.wheels
|
78
|
-
end
|
79
|
-
|
80
|
-
test '#to_model accepts a Hash of Symbols' do
|
81
|
-
args = { id: 1 }
|
82
|
-
model = @repository.to_model(args)
|
83
|
-
|
84
|
-
assert_equal 1, model.id
|
85
|
-
end
|
86
|
-
|
87
|
-
test '#to_model accepts a Hash of Strings' do
|
88
|
-
args = { 'id' => 1 }
|
89
|
-
model = @repository.to_model(args)
|
90
|
-
|
91
|
-
assert_equal 1, model.id
|
92
|
-
end
|
93
31
|
end
|
94
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: upgrow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify Engineering
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -35,7 +35,8 @@ files:
|
|
35
35
|
- lib/upgrow.rb
|
36
36
|
- lib/upgrow/action.rb
|
37
37
|
- lib/upgrow/actions.rb
|
38
|
-
- lib/upgrow/
|
38
|
+
- lib/upgrow/active_record_conversion.rb
|
39
|
+
- lib/upgrow/active_record_queries.rb
|
39
40
|
- lib/upgrow/active_record_schema.rb
|
40
41
|
- lib/upgrow/basic_model.rb
|
41
42
|
- lib/upgrow/basic_repository.rb
|
@@ -123,7 +124,8 @@ files:
|
|
123
124
|
- test/test_helper.rb
|
124
125
|
- test/upgrow/action_test.rb
|
125
126
|
- test/upgrow/actions_test.rb
|
126
|
-
- test/upgrow/
|
127
|
+
- test/upgrow/active_record_conversion_test.rb
|
128
|
+
- test/upgrow/active_record_queries_test.rb
|
127
129
|
- test/upgrow/active_record_schema_test.rb
|
128
130
|
- test/upgrow/basic_model_test.rb
|
129
131
|
- test/upgrow/basic_repository_test.rb
|
@@ -139,7 +141,7 @@ homepage: https://github.com/Shopify/upgrow
|
|
139
141
|
licenses:
|
140
142
|
- MIT
|
141
143
|
metadata:
|
142
|
-
source_code_uri: https://github.com/Shopify/upgrow/tree/v0.0.
|
144
|
+
source_code_uri: https://github.com/Shopify/upgrow/tree/v0.0.4
|
143
145
|
allowed_push_host: https://rubygems.org
|
144
146
|
post_install_message:
|
145
147
|
rdoc_options: []
|
@@ -236,7 +238,8 @@ test_files:
|
|
236
238
|
- test/test_helper.rb
|
237
239
|
- test/upgrow/action_test.rb
|
238
240
|
- test/upgrow/actions_test.rb
|
239
|
-
- test/upgrow/
|
241
|
+
- test/upgrow/active_record_conversion_test.rb
|
242
|
+
- test/upgrow/active_record_queries_test.rb
|
240
243
|
- test/upgrow/active_record_schema_test.rb
|
241
244
|
- test/upgrow/basic_model_test.rb
|
242
245
|
- test/upgrow/basic_repository_test.rb
|