upgrow 0.0.4 → 0.0.5

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