upgrow 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|