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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/lib/upgrow.rb +2 -8
  4. data/lib/upgrow/action.rb +66 -16
  5. data/lib/upgrow/actions.rb +31 -0
  6. data/lib/upgrow/active_record_adapter.rb +24 -15
  7. data/lib/upgrow/active_record_schema.rb +63 -0
  8. data/lib/upgrow/basic_model.rb +64 -0
  9. data/lib/upgrow/basic_repository.rb +49 -22
  10. data/lib/upgrow/error.rb +19 -0
  11. data/lib/upgrow/immutable_object.rb +26 -21
  12. data/lib/upgrow/input.rb +7 -0
  13. data/lib/upgrow/model.rb +12 -12
  14. data/lib/upgrow/model_schema.rb +31 -0
  15. data/lib/upgrow/repository.rb +3 -0
  16. data/lib/upgrow/result.rb +18 -55
  17. data/lib/upgrow/schema.rb +33 -0
  18. data/test/application_system_test_case.rb +11 -0
  19. data/test/dummy/app/actions/application_action.rb +10 -0
  20. data/test/dummy/app/actions/articles/create_action.rb +15 -0
  21. data/test/dummy/app/actions/articles/destroy_action.rb +9 -0
  22. data/test/dummy/app/actions/articles/edit_action.rb +12 -0
  23. data/test/dummy/app/actions/articles/index_action.rb +11 -0
  24. data/test/dummy/app/actions/articles/new_action.rb +8 -0
  25. data/test/dummy/app/actions/articles/show_action.rb +11 -0
  26. data/test/dummy/app/actions/articles/update_action.rb +15 -0
  27. data/test/dummy/app/actions/comments/create_action.rb +15 -0
  28. data/test/dummy/app/actions/comments/destroy_action.rb +9 -0
  29. data/test/dummy/app/actions/comments/new_action.rb +8 -0
  30. data/test/dummy/app/actions/sessions/create_action.rb +23 -0
  31. data/test/dummy/app/actions/sessions/destroy_action.rb +6 -0
  32. data/test/dummy/app/actions/sessions/new_action.rb +8 -0
  33. data/test/dummy/app/actions/user_action.rb +10 -0
  34. data/test/dummy/app/actions/users/create_action.rb +13 -0
  35. data/test/dummy/app/actions/users/new_action.rb +8 -0
  36. data/test/dummy/app/controllers/application_controller.rb +10 -0
  37. data/test/dummy/app/controllers/articles_controller.rb +32 -28
  38. data/test/dummy/app/controllers/comments_controller.rb +41 -0
  39. data/test/dummy/app/controllers/sessions_controller.rb +34 -0
  40. data/test/dummy/app/controllers/users_controller.rb +29 -0
  41. data/test/dummy/app/helpers/application_helper.rb +27 -3
  42. data/test/dummy/app/helpers/users_helper.rb +15 -0
  43. data/test/dummy/app/inputs/article_input.rb +3 -0
  44. data/test/dummy/app/inputs/comment_input.rb +11 -0
  45. data/test/dummy/app/inputs/session_input.rb +9 -0
  46. data/test/dummy/app/inputs/user_input.rb +9 -0
  47. data/test/dummy/app/models/article.rb +0 -2
  48. data/test/dummy/app/models/comment.rb +4 -0
  49. data/test/dummy/app/models/user.rb +4 -0
  50. data/test/dummy/app/records/article_record.rb +2 -0
  51. data/test/dummy/app/records/comment_record.rb +7 -0
  52. data/test/dummy/app/records/user_record.rb +9 -0
  53. data/test/dummy/app/repositories/article_repository.rb +26 -0
  54. data/test/dummy/app/repositories/comment_repository.rb +3 -0
  55. data/test/dummy/app/repositories/user_repository.rb +12 -0
  56. data/test/dummy/config/routes.rb +6 -1
  57. data/test/dummy/db/migrate/20210320140432_create_comments.rb +12 -0
  58. data/test/dummy/db/migrate/20210409164927_create_users.rb +22 -0
  59. data/test/dummy/db/schema.rb +24 -1
  60. data/test/system/articles_test.rb +87 -29
  61. data/test/system/comments_test.rb +81 -0
  62. data/test/system/guest_user_test.rb +14 -0
  63. data/test/system/sign_in_test.rb +57 -0
  64. data/test/system/sign_out_test.rb +19 -0
  65. data/test/system/sign_up_test.rb +38 -0
  66. data/test/test_helper.rb +6 -1
  67. data/test/upgrow/action_test.rb +101 -9
  68. data/test/upgrow/actions_test.rb +24 -0
  69. data/test/upgrow/active_record_adapter_test.rb +12 -17
  70. data/test/upgrow/active_record_schema_test.rb +92 -0
  71. data/test/upgrow/basic_model_test.rb +95 -0
  72. data/test/upgrow/basic_repository_test.rb +48 -27
  73. data/test/upgrow/immutable_object_test.rb +43 -7
  74. data/test/upgrow/input_test.rb +19 -1
  75. data/test/upgrow/model_schema_test.rb +44 -0
  76. data/test/upgrow/model_test.rb +48 -11
  77. data/test/upgrow/result_test.rb +19 -64
  78. data/test/upgrow/schema_test.rb +44 -0
  79. metadata +128 -50
  80. data/test/dummy/app/actions/create_article_action.rb +0 -13
  81. data/test/dummy/app/actions/delete_article_action.rb +0 -7
  82. data/test/dummy/app/actions/edit_article_action.rb +0 -10
  83. data/test/dummy/app/actions/list_articles_action.rb +0 -8
  84. data/test/dummy/app/actions/show_article_action.rb +0 -10
  85. data/test/dummy/app/actions/update_article_action.rb +0 -13
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comments
4
+ class CreateAction < ApplicationAction
5
+ expose :comment
6
+
7
+ def perform(input)
8
+ if input.valid?
9
+ @comment = CommentRepository.new.create(input)
10
+ else
11
+ failure(input.errors)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comments
4
+ class DestroyAction < ApplicationAction
5
+ def perform(id)
6
+ CommentRepository.new.delete(id)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comments
4
+ class NewAction < UserAction
5
+ def perform
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sessions
4
+ class CreateAction < ApplicationAction
5
+ def perform(input)
6
+ if input.valid?
7
+ @current_user = UserRepository.new.find_for_authentication(input)
8
+ unless @current_user
9
+ failure(
10
+ Upgrow::Error.new(
11
+ message: 'Invalid email or password.',
12
+ code: :invalid_email_or_password,
13
+ attribute: :password
14
+ ),
15
+ Upgrow::Error.new(attribute: :email)
16
+ )
17
+ end
18
+ else
19
+ failure(input.errors)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sessions
4
+ class DestroyAction < ApplicationAction
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sessions
4
+ class NewAction < ApplicationAction
5
+ def perform
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UserAction < ApplicationAction
4
+ class UnauthorizedError < StandardError; end
5
+
6
+ def initialize(...)
7
+ super
8
+ raise UnauthorizedError unless @current_user
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Users
4
+ class CreateAction < ApplicationAction
5
+ def perform(input)
6
+ if input.valid?
7
+ @current_user = UserRepository.new.create(input)
8
+ else
9
+ failure(input.errors)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Users
4
+ class NewAction < ApplicationAction
5
+ def perform
6
+ end
7
+ end
8
+ end
@@ -1,3 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
  class ApplicationController < ActionController::Base
3
+ rescue_from(UserAction::UnauthorizedError) { redirect_to(new_session_path) }
4
+
5
+ before_action :instantiate_action
6
+
7
+ private
8
+
9
+ def instantiate_action
10
+ action_class = Upgrow::Actions[controller_name, action_name]
11
+ @action = action_class.new(user_id: session[:user_id])
12
+ end
3
13
  end
@@ -2,57 +2,61 @@
2
2
 
3
3
  class ArticlesController < ApplicationController
4
4
  def index
5
- @articles = ListArticlesAction.new.perform.articles
5
+ @result = @action.perform
6
6
  end
7
7
 
8
8
  def show
9
- @article = ShowArticleAction.new.perform(params[:id]).article
9
+ @result = @action.perform(params[:id])
10
10
  end
11
11
 
12
12
  def new
13
13
  @input = ArticleInput.new
14
+ @result = @action.perform
14
15
  end
15
16
 
16
17
  def edit
17
- article = EditArticleAction.new.perform(params[:id]).article
18
+ @result = @action.perform(params[:id])
18
19
  @input = ArticleInput.new(
19
- title: article.title, body: article.body
20
+ title: @result.article.title, body: @result.article.body
20
21
  )
21
22
  end
22
23
 
23
24
  def create
24
- @input = ArticleInput.new(article_params)
25
+ @input = ArticleInput.new(
26
+ article_params.merge(user_id: session[:user_id])
27
+ )
28
+
29
+ @result = @action.perform(@input)
25
30
 
26
- CreateArticleAction.new.perform(@input)
27
- .and_then do |article:|
28
- redirect_to(
29
- article_path(article.id), notice: 'Article was successfully created.'
30
- )
31
- end
32
- .or_else do |errors|
33
- @errors = errors
34
- render(:new)
35
- end
31
+ if @result.success?
32
+ redirect_to(
33
+ article_path(@result.article.id),
34
+ notice: 'Article was successfully created.'
35
+ )
36
+ else
37
+ render(:new)
38
+ end
36
39
  end
37
40
 
38
41
  def update
39
- @input = ArticleInput.new(article_params)
42
+ @input = ArticleInput.new(
43
+ article_params.merge(user_id: session[:user_id])
44
+ )
45
+
46
+ @result = @action.perform(params[:id], @input)
40
47
 
41
- UpdateArticleAction.new.perform(params[:id], @input)
42
- .and_then do |article:|
43
- redirect_to(
44
- article_path(article.id),
45
- notice: 'Article was successfully updated.'
46
- )
47
- end
48
- .or_else do |errors|
49
- @errors = errors
50
- render(:edit)
51
- end
48
+ if @result.success?
49
+ redirect_to(
50
+ article_path(@result.article.id),
51
+ notice: 'Article was successfully updated.'
52
+ )
53
+ else
54
+ render(:edit)
55
+ end
52
56
  end
53
57
 
54
58
  def destroy
55
- DeleteArticleAction.new.perform(params[:id])
59
+ @action.perform(params[:id])
56
60
  redirect_to(articles_url, notice: 'Article was successfully destroyed.')
57
61
  end
58
62
 
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CommentsController < ApplicationController
4
+ def new
5
+ @input = CommentInput.new(article_id: params[:article_id])
6
+ @result = @action.perform
7
+ end
8
+
9
+ def create
10
+ @input = CommentInput.new(
11
+ comment_params.merge(article_id: params[:article_id],
12
+ user_id: session[:user_id])
13
+ )
14
+
15
+ @result = @action.perform(@input)
16
+
17
+ if @result.success?
18
+ redirect_to(
19
+ article_path(@result.comment.article_id),
20
+ notice: 'Comment was successfully created.'
21
+ )
22
+ else
23
+ render(:new)
24
+ end
25
+ end
26
+
27
+ def destroy
28
+ @action.perform(params[:id])
29
+ redirect_to(
30
+ article_path(params[:article_id]),
31
+ notice: 'Comment was successfully destroyed.'
32
+ )
33
+ end
34
+
35
+ private
36
+
37
+ # Only allow a list of trusted parameters through.
38
+ def comment_params
39
+ params.require(:comment_input).permit(:body)
40
+ end
41
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SessionsController < ApplicationController
4
+ def new
5
+ @input = SessionInput.new
6
+ @result = @action.perform
7
+ end
8
+
9
+ def create
10
+ @input = SessionInput.new(session_params)
11
+
12
+ @result = @action.perform(@input)
13
+
14
+ if @result.success?
15
+ reset_session
16
+ session[:user_id] = @result.current_user.id
17
+ redirect_to(root_path)
18
+ else
19
+ render(:new)
20
+ end
21
+ end
22
+
23
+ def destroy
24
+ reset_session
25
+ redirect_to(root_path)
26
+ end
27
+
28
+ private
29
+
30
+ # Only allow a list of trusted parameters through.
31
+ def session_params
32
+ params.require(:session_input).permit(:email, :password)
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UsersController < ApplicationController
4
+ def new
5
+ @input = UserInput.new
6
+ @result = @action.perform
7
+ end
8
+
9
+ def create
10
+ @input = UserInput.new(user_params)
11
+
12
+ @result = @action.perform(@input)
13
+
14
+ if @result.success?
15
+ reset_session
16
+ session[:user_id] = @result.current_user.id
17
+ redirect_to(root_path)
18
+ else
19
+ render(:new)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ # Only allow a list of trusted parameters through.
26
+ def user_params
27
+ params.require(:user_input).permit(:email, :password)
28
+ end
29
+ end
@@ -24,7 +24,9 @@ module ApplicationHelper
24
24
 
25
25
  if method
26
26
  errors_for(method).each do |error|
27
- result += @template.content_tag(:p, error, class: 'help is-danger')
27
+ result += @template.content_tag(
28
+ :p, error.message, class: 'help is-danger'
29
+ )
28
30
  end
29
31
  end
30
32
 
@@ -46,6 +48,7 @@ module ApplicationHelper
46
48
  def text_field(method, label: true, expanded: false, field: true, **args)
47
49
  input_class = ['input']
48
50
  input_class << 'is-danger' if errors_for(method).any?
51
+
49
52
  output = control(expanded: expanded) do
50
53
  super(method, class: input_class, **args)
51
54
  end
@@ -68,6 +71,28 @@ module ApplicationHelper
68
71
  field(method: method, label: true) { output }
69
72
  end
70
73
 
74
+ def email_field(method, **args)
75
+ input_class = ['input']
76
+ input_class << 'is-danger' if errors_for(method).any?
77
+
78
+ output = control(expanded: false) do
79
+ super(method, class: input_class, **args)
80
+ end
81
+
82
+ field(method: method, label: true) { output }
83
+ end
84
+
85
+ def password_field(method, **args)
86
+ input_class = ['input']
87
+ input_class << 'is-danger' if errors_for(method).any?
88
+
89
+ output = control(expanded: false) do
90
+ super(method, class: input_class, **args)
91
+ end
92
+
93
+ field(method: method, label: true) { output }
94
+ end
95
+
71
96
  # Render a form submit button.
72
97
  #
73
98
  # @param text [Symbol] the text for the button.
@@ -95,8 +120,7 @@ module ApplicationHelper
95
120
  end
96
121
 
97
122
  def errors_for(method)
98
- (options[:errors] || ActiveModel::Errors.new(self))
99
- .full_messages_for(method)
123
+ (options[:errors] || []).select { |error| error.attribute == method }
100
124
  end
101
125
  end
102
126
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UsersHelper
4
+ def user_signed_in?
5
+ current_user.present?
6
+ end
7
+
8
+ def current_user
9
+ @result.current_user
10
+ end
11
+
12
+ def belongs_to_current_user?(model)
13
+ user_signed_in? && model.user_id == current_user.id
14
+ end
15
+ end
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class ArticleInput < Upgrow::Input
3
4
  attribute :title
4
5
  attribute :body
6
+ attribute :user_id
5
7
 
8
+ validates :user_id, presence: true
6
9
  validates :title, presence: true
7
10
  validates :body, presence: true, length: { minimum: 10 }
8
11
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CommentInput < Upgrow::Input
4
+ attribute :article_id
5
+ attribute :user_id
6
+ attribute :body
7
+
8
+ validates :article_id, presence: true
9
+ validates :user_id, presence: true
10
+ validates :body, presence: true, length: { minimum: 10 }
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SessionInput < Upgrow::Input
4
+ attribute :email
5
+ attribute :password
6
+
7
+ validates :email, presence: true
8
+ validates :password, presence: true
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UserInput < Upgrow::Input
4
+ attribute :email
5
+ attribute :password
6
+
7
+ validates :email, presence: true
8
+ validates :password, presence: true
9
+ end