upgrow 0.0.4 → 0.0.5

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/test_unit/input/input_generator.rb +22 -0
  3. data/lib/generators/test_unit/input/templates/input_test.rb.tt +8 -0
  4. data/lib/generators/test_unit/install/install_generator.rb +48 -0
  5. data/lib/generators/test_unit/record/record_generator.rb +22 -0
  6. data/lib/generators/test_unit/record/templates/record_test.rb.tt +8 -0
  7. data/lib/generators/test_unit/repository/repository_generator.rb +23 -0
  8. data/lib/generators/test_unit/repository/templates/repository_test.rb.tt +8 -0
  9. data/lib/generators/upgrow.rb +54 -0
  10. data/lib/generators/upgrow/action/USAGE +13 -0
  11. data/lib/generators/upgrow/action/action_generator.rb +24 -0
  12. data/lib/generators/upgrow/action/templates/action.rb.tt +9 -0
  13. data/lib/generators/upgrow/input/USAGE +10 -0
  14. data/lib/generators/upgrow/input/input_generator.rb +26 -0
  15. data/lib/generators/upgrow/input/templates/input.rb.tt +6 -0
  16. data/lib/generators/upgrow/install/USAGE +25 -0
  17. data/lib/generators/upgrow/install/install_generator.rb +56 -0
  18. data/lib/generators/upgrow/install/templates/app/actions/application_action.rb.tt +4 -0
  19. data/lib/generators/upgrow/install/templates/app/inputs/application_input.rb.tt +4 -0
  20. data/lib/generators/upgrow/install/templates/app/models/application_model.rb.tt +4 -0
  21. data/lib/generators/upgrow/install/templates/app/repositories/application_repository.rb.tt +4 -0
  22. data/lib/generators/upgrow/model/USAGE +10 -0
  23. data/lib/generators/upgrow/model/model_generator.rb +24 -0
  24. data/lib/generators/upgrow/model/templates/model.rb.tt +6 -0
  25. data/lib/generators/upgrow/record/record_generator.rb +34 -0
  26. data/lib/generators/upgrow/record/templates/record.rb.tt +6 -0
  27. data/lib/generators/upgrow/repository/USAGE +10 -0
  28. data/lib/generators/upgrow/repository/repository_generator.rb +27 -0
  29. data/lib/generators/upgrow/repository/templates/repository.rb.tt +6 -0
  30. data/lib/upgrow.rb +3 -0
  31. data/lib/upgrow/active_record_conversion.rb +2 -3
  32. data/lib/upgrow/active_record_queries.rb +1 -2
  33. data/lib/upgrow/active_record_schema.rb +1 -3
  34. data/lib/upgrow/basic_model.rb +11 -0
  35. data/lib/upgrow/model.rb +2 -1
  36. data/lib/upgrow/naming.rb +40 -0
  37. data/lib/upgrow/railtie.rb +9 -0
  38. data/lib/upgrow/record.rb +121 -0
  39. data/test/dummy/app/records/application_record.rb +2 -0
  40. data/test/dummy/app/records/article_record.rb +2 -3
  41. data/test/dummy/app/records/comment_record.rb +2 -3
  42. data/test/dummy/app/records/user_record.rb +2 -4
  43. data/test/dummy/app/repositories/article_repository.rb +3 -3
  44. data/test/test_unit/generators/input_generator_test.rb +56 -0
  45. data/test/test_unit/generators/install_generator_test.rb +138 -0
  46. data/test/test_unit/generators/record_generator_test.rb +56 -0
  47. data/test/test_unit/generators/repository_generator_test.rb +57 -0
  48. data/test/upgrow/active_record_conversion_test.rb +2 -2
  49. data/test/upgrow/active_record_schema_test.rb +1 -1
  50. data/test/upgrow/basic_model_test.rb +85 -0
  51. data/test/upgrow/generators/action_generator_test.rb +59 -0
  52. data/test/upgrow/generators/helper_test.rb +86 -0
  53. data/test/upgrow/generators/input_generator_test.rb +65 -0
  54. data/test/upgrow/generators/install_generator_test.rb +158 -0
  55. data/test/upgrow/generators/model_generator_test.rb +39 -0
  56. data/test/upgrow/generators/record_generator_test.rb +65 -0
  57. data/test/upgrow/generators/repository_generator_test.rb +50 -0
  58. data/test/upgrow/naming_test.rb +22 -0
  59. data/test/upgrow/record_test.rb +176 -0
  60. metadata +60 -3
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= class_name %> < ApplicationModel
5
+ end
6
+ <% end -%>
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'generators/upgrow'
4
+
5
+ module Upgrow
6
+ module Generators
7
+ class RecordGenerator < Rails::Generators::NamedBase
8
+ include Helper
9
+
10
+ def create_record_file
11
+ template(
12
+ 'record.rb',
13
+ File.join('app/records', class_path, "#{file_name}_record.rb")
14
+ )
15
+ end
16
+
17
+ hook_for :migration,
18
+ default: :active_record,
19
+ as: :migration do |instance, generator|
20
+ instance.invoke(
21
+ generator, ["create_#{instance.send(:table_name)}", instance.args]
22
+ )
23
+ end
24
+
25
+ hook_for :test_framework
26
+
27
+ private
28
+
29
+ def file_name
30
+ super.delete_suffix('_record')
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= class_name %>Record < ApplicationRecord
5
+ end
6
+ <% end -%>
@@ -0,0 +1,10 @@
1
+ Description:
2
+
3
+ Creates a new Repository class with the given name under `app/repositories`.
4
+
5
+ Example:
6
+
7
+ `bin/rails g upgrow:repository article`
8
+
9
+ create app/repositories/article_repository.rb
10
+ invoke test_unit
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'generators/upgrow'
4
+
5
+ module Upgrow
6
+ module Generators
7
+ class RepositoryGenerator < Rails::Generators::NamedBase
8
+ include Helper
9
+
10
+ def create_repository_file
11
+ template(
12
+ 'repository.rb',
13
+ File.join('app/repositories', class_path,
14
+ "#{file_name}_repository.rb")
15
+ )
16
+ end
17
+
18
+ hook_for :test_framework
19
+
20
+ private
21
+
22
+ def file_name
23
+ super.delete_suffix('_repository')
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= class_name %>Repository < ApplicationRepository
5
+ end
6
+ <% end -%>
data/lib/upgrow.rb CHANGED
@@ -4,8 +4,11 @@ require_relative 'upgrow/action'
4
4
  require_relative 'upgrow/actions'
5
5
  require_relative 'upgrow/input'
6
6
  require_relative 'upgrow/model'
7
+ require_relative 'upgrow/record'
7
8
  require_relative 'upgrow/repository'
8
9
 
10
+ require 'upgrow/railtie' if defined?(Rails::Railtie)
11
+
9
12
  # The gem's main namespace.
10
13
  module Upgrow
11
14
  end
@@ -29,11 +29,10 @@ module Upgrow
29
29
  association = record.association(reflection.to_sym)
30
30
  next unless association.loaded?
31
31
 
32
- name = reflection.sub('_record', '').to_sym
33
- [name, to_model(record.public_send(reflection))]
32
+ [reflection.to_sym, to_model(record.public_send(reflection))]
34
33
  end.compact.to_h
35
34
 
36
- model_class = Object.const_get(record.class.name.delete_suffix('Record'))
35
+ model_class = Object.const_get(Naming.record_to_model(record.class.name))
37
36
 
38
37
  attributes = record.attributes.merge(associations)
39
38
 
@@ -19,8 +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]}Record"
23
- Object.const_get(base_name)
22
+ Object.const_get(Naming.repository_to_record(name))
24
23
  end
25
24
  end
26
25
 
@@ -47,9 +47,7 @@ module Upgrow
47
47
  #
48
48
  # @return [Array<Symbol>] the list of attribute names.
49
49
  def association_names
50
- association_names = base.reflections.keys.map do |key|
51
- key.sub('_record', '').to_sym
52
- end
50
+ association_names = base.reflections.keys.map(&:to_sym)
53
51
 
54
52
  association_names | @default_schema.association_names
55
53
  end
@@ -50,6 +50,17 @@ module Upgrow
50
50
  super(**attributes)
51
51
  end
52
52
 
53
+ # Returns a Hash representation of Model along with all loaded associations.
54
+ #
55
+ # @return [Hash<Symbol, Object>] the list of attribute names
56
+ # and associations, if loaded.
57
+ def to_h
58
+ associations_hash = associations.compact.transform_values do |models|
59
+ models.respond_to?(:map) ? models.map(&:to_h) : models.to_h
60
+ end
61
+ attributes.merge(associations_hash)
62
+ end
63
+
53
64
  private
54
65
 
55
66
  def method_missing(name, *args, &block)
data/lib/upgrow/model.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'active_record_schema'
4
4
  require_relative 'basic_model'
5
+ require_relative 'naming'
5
6
 
6
7
  module Upgrow
7
8
  # Models are objects that represent core entities of the app's business logic.
@@ -27,7 +28,7 @@ module Upgrow
27
28
  super
28
29
 
29
30
  subclass.schema = ActiveRecordSchema.new(
30
- "#{subclass.name}Record", subclass.schema
31
+ Naming.model_to_record(subclass.name), subclass.schema
31
32
  )
32
33
  end
33
34
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Upgrow
4
+ # Functions to derive constant names and other identifiers according to
5
+ # Upgrow's conventions.
6
+ module Naming
7
+ extend self
8
+
9
+ # Convert a Model name to a Record name. The Record name by convention is
10
+ # the Model name with a `Record` suffix.
11
+ #
12
+ # @param model_name [String] the Model name.
13
+ #
14
+ # @return [String] the Record name.
15
+ def model_to_record(model_name)
16
+ "#{model_name}Record"
17
+ end
18
+
19
+ # Convert a Record name to a Model name. The Model name by convention is the
20
+ # Record name without the `Record` suffix.
21
+ #
22
+ # @param record_name [String] the Record name.
23
+ #
24
+ # @return [String] the Model name.
25
+ def record_to_model(record_name)
26
+ record_name.delete_suffix('Record')
27
+ end
28
+
29
+ # Convert a Repository name to a Record name. The Record name is inferred by
30
+ # the Repository name without the `Repository` suffix, and with the `Record`
31
+ # suffix added.
32
+ #
33
+ # @param repository_name [String] the Repository name.
34
+ #
35
+ # @return [String] the Record name.
36
+ def repository_to_record(repository_name)
37
+ "#{repository_name.delete_suffix("Repository")}Record"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Upgrow
4
+ # Upgrow's Railtie. This class allows Upgrow to change Rails' default configs.
5
+ class Railtie < Rails::Railtie
6
+ config.app_generators.orm(:upgrow)
7
+ config.app_generators.fallbacks[:upgrow] = :active_record
8
+ end
9
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Upgrow
4
+ # Utility mixin to help adjust Active Record Base classes according to
5
+ # Upgrow's conventions. It allows the use of the `Record` suffix in class
6
+ # naming while keeping it out of table and association names, among other
7
+ # adaptations.
8
+ module Record
9
+ # Class methods to be extended by the class that includes the Record module.
10
+ module ClassMethods
11
+ # Overwrites the table name reader from ActiveRecord::ModelSchema,
12
+ # removing the `records` suffix by default while still allowing table name
13
+ # to be set explicitly.
14
+ #
15
+ # See https://api.rubyonrails.org/classes/ActiveRecord/ModelSchema/ClassMethods.html
16
+ def table_name
17
+ return @table_name if defined?(@table_name)
18
+
19
+ self.table_name = super&.sub('_record', '')
20
+ end
21
+
22
+ # Overwrites the has many association macro to adjust the default class
23
+ # and foreign key names to Upgrow's conventions.
24
+ #
25
+ # The default class name for the associated collection of Records is based
26
+ # on the association name and the `Record` suffix. The association's
27
+ # foreign key is derived from the caller's model name.
28
+ #
29
+ # All these values can be set explicitly according to Active Record's
30
+ # original behaviour.
31
+ #
32
+ # See https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
33
+ def has_many(name, scope = nil, **options, &extension)
34
+ options = apply_default_options(name, options)
35
+
36
+ unless options.key?(:as)
37
+ options[:foreign_key] ||= foreign_key(model_name)
38
+ end
39
+
40
+ super
41
+ end
42
+
43
+ # Overwrites the has one association macro to adjust the default class
44
+ # and foreign key names to Upgrow's conventions.
45
+ #
46
+ # The default class name for the associated Record is based on the
47
+ # association name and the `Record` suffix. The association's foreign key
48
+ # is derived from the caller's model name.
49
+ #
50
+ # All these values can be set explicitly according to Active Record's
51
+ # original behaviour.
52
+ #
53
+ # See https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
54
+ def has_one(name, scope = nil, **options, &extension)
55
+ options = apply_default_options(name, options)
56
+
57
+ unless options.key?(:as)
58
+ options[:foreign_key] ||= foreign_key(model_name)
59
+ end
60
+
61
+ super
62
+ end
63
+
64
+ # Overwrites the belongs to association macro to adjust the default class
65
+ # and foreign key names to Upgrow's conventions.
66
+ #
67
+ # The default class name for the associated Record is based on the
68
+ # association name and the `Record` suffix.
69
+ #
70
+ # All these values can be set explicitly according to Active Record's
71
+ # original behaviour.
72
+ #
73
+ # See https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
74
+ def belongs_to(name, scope = nil, **options)
75
+ options = apply_default_options(name, options)
76
+ super
77
+ end
78
+
79
+ # Overwrites the has and belongs to many association macro to adjust the
80
+ # default class and foreign key names to Upgrow's conventions.
81
+ #
82
+ # The default class name for the associated Record is based on the
83
+ # association name and the `Record` suffix. The association's foreign key
84
+ # is derived from the caller's model name.
85
+ #
86
+ # All these values can be set explicitly according to Active Record's
87
+ # original behaviour.
88
+ #
89
+ # See https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
90
+ def has_and_belongs_to_many(name, scope = nil, **options, &extension)
91
+ options = apply_default_options(name, options)
92
+
93
+ options[:foreign_key] ||= foreign_key(model_name)
94
+
95
+ options[:association_foreign_key] ||=
96
+ foreign_key(options.fetch(:class_name))
97
+
98
+ super
99
+ end
100
+
101
+ # @private
102
+ def apply_default_options(name, options)
103
+ default_options = {
104
+ class_name: Naming.model_to_record(name.to_s.singularize.camelize),
105
+ }
106
+
107
+ default_options.merge(options)
108
+ end
109
+
110
+ # @private
111
+ def foreign_key(record_name)
112
+ Naming.record_to_model(record_name.to_s).foreign_key
113
+ end
114
+ end
115
+
116
+ # @private
117
+ def self.included(base)
118
+ base.extend(ClassMethods)
119
+ end
120
+ end
121
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ApplicationRecord < ActiveRecord::Base
4
+ include Upgrow::Record
5
+
4
6
  self.abstract_class = true
5
7
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ArticleRecord < ApplicationRecord
4
- self.table_name = 'articles'
5
- has_many :comment_records, foreign_key: :article_id, dependent: :destroy
6
- belongs_to :user_record, foreign_key: :user_id
4
+ has_many :comments, dependent: :destroy
5
+ belongs_to :user
7
6
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class CommentRecord < ApplicationRecord
4
- self.table_name = 'comments'
5
- belongs_to :user_record, foreign_key: :user_id
6
- belongs_to :article_record, foreign_key: :article_id
4
+ belongs_to :user
5
+ belongs_to :article
7
6
  end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UserRecord < ApplicationRecord
4
- self.table_name = 'users'
5
-
6
4
  has_secure_password
7
5
 
8
- has_many :article_records, foreign_key: :user_id, dependent: :destroy
9
- has_many :comment_records, foreign_key: :user_id, dependent: :destroy
6
+ has_many :articles, dependent: :destroy
7
+ has_many :comments, dependent: :destroy
10
8
  end
@@ -2,13 +2,13 @@
2
2
 
3
3
  class ArticleRepository < Upgrow::Repository
4
4
  def all
5
- to_model(base.all.includes(:user_record))
5
+ to_model(base.all.includes(:user))
6
6
  end
7
7
 
8
8
  def find_with_comments(id)
9
9
  record = base
10
- .includes(:user_record)
11
- .includes(comment_records: :user_record)
10
+ .includes(:user)
11
+ .includes(comments: :user)
12
12
  .find(id)
13
13
 
14
14
  to_model(record)
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'rails/generators/test_case'
5
+ require 'generators/test_unit/input/input_generator'
6
+
7
+ module TestUnit
8
+ module Generators
9
+ class InputGeneratorTest < Rails::Generators::TestCase
10
+ tests InputGenerator
11
+ destination File.expand_path('tmp')
12
+ setup :prepare_destination
13
+
14
+ test 'generate Input test file' do
15
+ run_generator(['article'])
16
+
17
+ assert_file 'test/inputs/article_input_test.rb', <<~RUBY
18
+ # frozen_string_literal: true
19
+
20
+ require 'test_helper'
21
+
22
+ class ArticleInputTest < ActiveSupport::TestCase
23
+ end
24
+ RUBY
25
+ end
26
+
27
+ test 'generate Input test file when suffix is specified' do
28
+ run_generator(['ArticleInput'])
29
+
30
+ assert_file 'test/inputs/article_input_test.rb', <<~RUBY
31
+ # frozen_string_literal: true
32
+
33
+ require 'test_helper'
34
+
35
+ class ArticleInputTest < ActiveSupport::TestCase
36
+ end
37
+ RUBY
38
+ end
39
+
40
+ test 'generate namespaced Input test file' do
41
+ run_generator(['articles/article'])
42
+
43
+ assert_file 'test/inputs/articles/article_input_test.rb', <<~RUBY
44
+ # frozen_string_literal: true
45
+
46
+ require 'test_helper'
47
+
48
+ module Articles
49
+ class ArticleInputTest < ActiveSupport::TestCase
50
+ end
51
+ end
52
+ RUBY
53
+ end
54
+ end
55
+ end
56
+ end