rider-kick 0.0.13 → 0.0.14

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +629 -25
  3. data/lib/generators/rider_kick/USAGE +2 -0
  4. data/lib/generators/rider_kick/base_generator.rb +190 -0
  5. data/lib/generators/rider_kick/clean_arch_generator.rb +235 -45
  6. data/lib/generators/rider_kick/clean_arch_generator_engine_spec.rb +359 -0
  7. data/lib/generators/rider_kick/clean_arch_generator_factory_bot_spec.rb +131 -0
  8. data/lib/generators/rider_kick/entity_type_mapping_spec.rb +22 -13
  9. data/lib/generators/rider_kick/errors.rb +42 -0
  10. data/lib/generators/rider_kick/factory_generator.rb +238 -0
  11. data/lib/generators/rider_kick/factory_generator_spec.rb +175 -0
  12. data/lib/generators/rider_kick/repositories_contract_spec.rb +95 -22
  13. data/lib/generators/rider_kick/scaffold_generator.rb +377 -62
  14. data/lib/generators/rider_kick/scaffold_generator_builder_uploaders_spec.rb +119 -14
  15. data/lib/generators/rider_kick/scaffold_generator_conditional_filtering_spec.rb +820 -0
  16. data/lib/generators/rider_kick/scaffold_generator_contracts_spec.rb +37 -10
  17. data/lib/generators/rider_kick/scaffold_generator_contracts_with_scope_spec.rb +40 -11
  18. data/lib/generators/rider_kick/scaffold_generator_engine_spec.rb +221 -0
  19. data/lib/generators/rider_kick/scaffold_generator_idempotent_spec.rb +38 -13
  20. data/lib/generators/rider_kick/scaffold_generator_list_spec_format_spec.rb +153 -0
  21. data/lib/generators/rider_kick/scaffold_generator_rspec_spec.rb +347 -0
  22. data/lib/generators/rider_kick/scaffold_generator_success_spec.rb +31 -12
  23. data/lib/generators/rider_kick/scaffold_generator_with_scope_spec.rb +32 -11
  24. data/lib/generators/rider_kick/structure_generator.rb +154 -43
  25. data/lib/generators/rider_kick/structure_generator_comprehensive_spec.rb +598 -0
  26. data/lib/generators/rider_kick/structure_generator_engine_spec.rb +279 -0
  27. data/lib/generators/rider_kick/structure_generator_spec.rb +3 -3
  28. data/lib/generators/rider_kick/structure_generator_success_spec.rb +33 -5
  29. data/lib/generators/rider_kick/structure_generator_unit_spec.rb +2202 -0
  30. data/lib/generators/rider_kick/templates/.rubocop.yml +5 -4
  31. data/lib/generators/rider_kick/templates/config/initializers/version.rb.tt +1 -1
  32. data/lib/generators/rider_kick/templates/db/migrate/20220613145533_init_database.rb +1 -1
  33. data/lib/generators/rider_kick/templates/db/structures/example.yaml.tt +140 -66
  34. data/lib/generators/rider_kick/templates/domains/core/builders/builder.rb.tt +36 -10
  35. data/lib/generators/rider_kick/templates/domains/core/builders/builder_spec.rb.tt +219 -0
  36. data/lib/generators/rider_kick/templates/domains/core/builders/error.rb.tt +2 -2
  37. data/lib/generators/rider_kick/templates/domains/core/builders/pagination.rb.tt +2 -2
  38. data/lib/generators/rider_kick/templates/domains/core/entities/entity.rb.tt +32 -14
  39. data/lib/generators/rider_kick/templates/domains/core/entities/error.rb.tt +1 -1
  40. data/lib/generators/rider_kick/templates/domains/core/entities/pagination.rb.tt +1 -1
  41. data/lib/generators/rider_kick/templates/domains/core/repositories/abstract_repository.rb.tt +4 -4
  42. data/lib/generators/rider_kick/templates/domains/core/repositories/create.rb.tt +2 -2
  43. data/lib/generators/rider_kick/templates/domains/core/repositories/create_spec.rb.tt +78 -0
  44. data/lib/generators/rider_kick/templates/domains/core/repositories/destroy.rb.tt +2 -2
  45. data/lib/generators/rider_kick/templates/domains/core/repositories/destroy_spec.rb.tt +88 -0
  46. data/lib/generators/rider_kick/templates/domains/core/repositories/fetch_by_id.rb.tt +3 -3
  47. data/lib/generators/rider_kick/templates/domains/core/repositories/fetch_by_id_spec.rb.tt +62 -0
  48. data/lib/generators/rider_kick/templates/domains/core/repositories/list.rb.tt +13 -8
  49. data/lib/generators/rider_kick/templates/domains/core/repositories/list_spec.rb.tt +190 -0
  50. data/lib/generators/rider_kick/templates/domains/core/repositories/update.rb.tt +4 -4
  51. data/lib/generators/rider_kick/templates/domains/core/repositories/update_spec.rb.tt +119 -0
  52. data/lib/generators/rider_kick/templates/domains/core/use_cases/contract/default.rb.tt +1 -1
  53. data/lib/generators/rider_kick/templates/domains/core/use_cases/contract/pagination.rb.tt +1 -1
  54. data/lib/generators/rider_kick/templates/domains/core/use_cases/create.rb.tt +3 -7
  55. data/lib/generators/rider_kick/templates/domains/core/use_cases/create_spec.rb.tt +71 -0
  56. data/lib/generators/rider_kick/templates/domains/core/use_cases/destroy.rb.tt +3 -7
  57. data/lib/generators/rider_kick/templates/domains/core/use_cases/destroy_spec.rb.tt +62 -0
  58. data/lib/generators/rider_kick/templates/domains/core/use_cases/fetch_by_id.rb.tt +3 -7
  59. data/lib/generators/rider_kick/templates/domains/core/use_cases/fetch_by_id_spec.rb.tt +62 -0
  60. data/lib/generators/rider_kick/templates/domains/core/use_cases/get_version.rb.tt +2 -2
  61. data/lib/generators/rider_kick/templates/domains/core/use_cases/list.rb.tt +3 -7
  62. data/lib/generators/rider_kick/templates/domains/core/use_cases/list_spec.rb.tt +64 -0
  63. data/lib/generators/rider_kick/templates/domains/core/use_cases/update.rb.tt +3 -7
  64. data/lib/generators/rider_kick/templates/domains/core/use_cases/update_spec.rb.tt +73 -0
  65. data/lib/generators/rider_kick/templates/domains/core/utils/abstract_utils.rb.tt +3 -3
  66. data/lib/generators/rider_kick/templates/domains/core/utils/request_methods.rb.tt +1 -1
  67. data/lib/generators/rider_kick/templates/env.development +1 -1
  68. data/lib/generators/rider_kick/templates/env.production +1 -1
  69. data/lib/generators/rider_kick/templates/env.test +1 -1
  70. data/lib/generators/rider_kick/templates/models/{application_record.rb → application_record.rb.tt} +3 -1
  71. data/lib/generators/rider_kick/templates/models/model_spec.rb.tt +68 -0
  72. data/lib/generators/rider_kick/templates/spec/factories/.gitkeep +19 -0
  73. data/lib/generators/rider_kick/templates/spec/factories/factory.rb.tt +8 -0
  74. data/lib/generators/rider_kick/templates/spec/rails_helper.rb +2 -0
  75. data/lib/generators/rider_kick/templates/spec/support/class_stubber.rb +148 -0
  76. data/lib/generators/rider_kick/templates/spec/support/factory_bot.rb +34 -0
  77. data/lib/generators/rider_kick/templates/spec/support/faker.rb +61 -0
  78. data/lib/rider-kick.rb +8 -6
  79. data/lib/rider_kick/builders/abstract_active_record_entity_builder_spec.rb +644 -0
  80. data/lib/rider_kick/configuration.rb +238 -0
  81. data/lib/rider_kick/configuration_engine_spec.rb +377 -0
  82. data/lib/rider_kick/entities/failure_details.rb +1 -1
  83. data/lib/rider_kick/entities/failure_details_spec.rb +1 -1
  84. data/lib/rider_kick/matchers/use_case_result.rb +1 -1
  85. data/lib/rider_kick/use_cases/abstract_use_case.rb +1 -1
  86. data/lib/rider_kick/version.rb +1 -1
  87. metadata +129 -8
  88. data/CHANGELOG.md +0 -5
@@ -1,7 +1,7 @@
1
1
  BASE_URL="http://localhost:3000"
2
2
 
3
3
  # database primary
4
- DATABASE_NAME=<%= Rails.application.class.module_parent_name.underscore %>_development
4
+ DATABASE_NAME=<%= (Rails.application&.class&.module_parent_name || 'myapp').underscore %>_production
5
5
  DATABASE_USERNAME=postgres
6
6
  DATABASE_PASSWORD=bayangan
7
7
  DATABASE_HOSTNAME=localhost
@@ -1,7 +1,7 @@
1
1
  BASE_URL="http://localhost:3000"
2
2
 
3
3
  # database primary
4
- DATABASE_NAME=<%= Rails.application.class.module_parent_name.underscore %>_development
4
+ DATABASE_NAME=<%= (Rails.application&.class&.module_parent_name || 'myapp').underscore %>_test
5
5
  DATABASE_USERNAME=postgres
6
6
  DATABASE_PASSWORD=bayangan
7
7
  DATABASE_HOSTNAME=localhost
@@ -1,4 +1,6 @@
1
- class ApplicationRecord < ActiveRecord::Base
1
+ # frozen_string_literal: true
2
+
3
+ class <%= domain_class_name_for_application_record %>::ApplicationRecord < ActiveRecord::Base
2
4
  primary_abstract_class
3
5
  self.abstract_class = true
4
6
 
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe <%= @model_class %>, type: :model do
6
+ <% if @uploaders.present? -%>
7
+ describe 'Active Storage attachments', :aggregate_failures do
8
+ <% @uploaders.each do |uploader| -%>
9
+ <% if uploader.type == 'single' -%>
10
+ it 'has one <%= uploader.name %> attached' do
11
+ expect(<%= @model_class %>.new.<%= uploader.name %>).to be_an_instance_of(ActiveStorage::Attached::One)
12
+ end
13
+
14
+ it 'can attach <%= uploader.name %>' do
15
+ <%= @variable_subject %> = <%= @model_class %>.new
16
+ <%= @variable_subject %>.<%= uploader.name %>.attach(
17
+ io: StringIO.new('test'),
18
+ filename: '<%= uploader.name %>.jpg',
19
+ content_type: 'image/jpeg'
20
+ )
21
+ expect(<%= @variable_subject %>.<%= uploader.name %>).to be_attached
22
+ end
23
+ <% else -%>
24
+ it 'has many <%= uploader.name %> attached' do
25
+ expect(<%= @model_class %>.new.<%= uploader.name %>).to be_an_instance_of(ActiveStorage::Attached::Many)
26
+ end
27
+
28
+ it 'can attach multiple <%= uploader.name %>' do
29
+ <%= @variable_subject %> = <%= @model_class %>.new
30
+ <%= @variable_subject %>.<%= uploader.name %>.attach(
31
+ [
32
+ { io: StringIO.new('test1'), filename: '<%= uploader.name %>_1.jpg', content_type: 'image/jpeg' },
33
+ { io: StringIO.new('test2'), filename: '<%= uploader.name %>_2.jpg', content_type: 'image/jpeg' }
34
+ ]
35
+ )
36
+ expect(<%= @variable_subject %>.<%= uploader.name %>.count).to eq(2)
37
+ end
38
+ <% end -%>
39
+
40
+ <% end -%>
41
+ end
42
+
43
+ <% end -%>
44
+
45
+ <% if @structure.schema&.enums.present? -%>
46
+ describe 'enums' do
47
+ <% @structure.schema.enums.each do |enum_name, enum_values| -%>
48
+ it { is_expected.to define_enum_for(:<%= enum_name %>).with_values(<%= enum_values.inspect %>) }
49
+ <% end -%>
50
+ end
51
+
52
+ <% end -%>
53
+ describe 'database columns' do
54
+ <% @columns_meta.each do |column| -%>
55
+ it 'has <%= column[:name] %> column' do
56
+ expect(<%= @model_class %>.column_names).to include('<%= column[:name] %>')
57
+ end
58
+
59
+ it 'has <%= column[:name] %> column of type <%= column[:type] %>' do
60
+ column = <%= @model_class %>.columns.find { |c| c.name == '<%= column[:name] %>' }
61
+ expect(column&.type).to eq(:<%= column[:type] %>)
62
+ end
63
+ <% end -%>
64
+ end
65
+ end
66
+
67
+
68
+
@@ -0,0 +1,19 @@
1
+ # This file ensures the factories directory is tracked by git
2
+ # Place your FactoryBot factory definitions here
3
+ #
4
+ # Example:
5
+ # FactoryBot.define do
6
+ # factory :user, class: 'Models::User' do
7
+ # id { SecureRandom.uuid }
8
+ # email { Faker::Internet.email }
9
+ # name { Faker::Name.name }
10
+ # created_at { Time.current }
11
+ # updated_at { Time.current }
12
+ # end
13
+ # end
14
+
15
+
16
+
17
+
18
+
19
+
@@ -0,0 +1,8 @@
1
+ FactoryBot.define do
2
+ factory :<%= @factory_name %>, class: '<%= @model_name %>' do
3
+ <% @attributes.each do |column| -%>
4
+ <%= column.name %> { <%= generate_faker_value(column) %> }
5
+ <% end -%>
6
+ end
7
+ end
8
+
@@ -78,10 +78,12 @@ RSpec.configure do |config|
78
78
  # config.filter_gems_from_backtrace("gem name")
79
79
 
80
80
  config.include FactoryBot::Syntax::Methods
81
+ config.include FakerHelpers
81
82
  config.include Dry::Monads[:result]
82
83
  config.after :suite do
83
84
  FileUtils.rm_rf(Dir[Rails.root.join('public', 'uploads')])
84
85
  end
85
86
  config.include FileStubber
87
+ config.include ClassStubber
86
88
  config.include RepositoryStubber
87
89
  end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ClassStubber provides utilities for creating test doubles of ActiveRecord models
4
+ # and ActiveStorage attachments in a clean, hash-based way.
5
+ #
6
+ # Usage:
7
+ # model = ClassStubber::Model.new(
8
+ # 'id' => '123',
9
+ # 'name' => 'Test Name',
10
+ # 'avatar' => ClassStubber::ActiveStorageAttachment.new_single('http://example.com/avatar.jpg')
11
+ # )
12
+ module ClassStubber
13
+ # Model stub that responds to hash keys as methods
14
+ class Model
15
+ def initialize(attributes = {})
16
+ @attributes = attributes.transform_keys(&:to_s)
17
+ end
18
+
19
+ def method_missing(method_name, *args, &block)
20
+ key = method_name.to_s
21
+ if @attributes.key?(key)
22
+ @attributes[key]
23
+ else
24
+ super
25
+ end
26
+ end
27
+
28
+ def respond_to_missing?(method_name, include_private = false)
29
+ @attributes.key?(method_name.to_s) || super
30
+ end
31
+
32
+ def [](key)
33
+ @attributes[key.to_s]
34
+ end
35
+
36
+ def []=(key, value)
37
+ @attributes[key.to_s] = value
38
+ end
39
+
40
+ def to_h
41
+ @attributes
42
+ end
43
+
44
+ def attributes
45
+ @attributes
46
+ end
47
+ end
48
+
49
+ # ActiveStorage attachment stub for single file attachments
50
+ class ActiveStorageAttachment
51
+ # Creates a stub for a single file attachment (has_one_attached)
52
+ #
53
+ # @param url [String, nil] The URL of the attached file, or nil if not attached
54
+ # @return [ActiveStorageAttachmentSingle] A stub that responds to attached? and url
55
+ def self.new_single(url)
56
+ ActiveStorageAttachmentSingle.new(url)
57
+ end
58
+
59
+ # Creates a stub for multiple file attachments (has_many_attached)
60
+ #
61
+ # @param urls [Array<String>] Array of URLs for attached files
62
+ # @return [ActiveStorageAttachmentMultiple] A stub that responds to attached? and can be enumerated
63
+ def self.new_multiple(urls = [])
64
+ ActiveStorageAttachmentMultiple.new(urls)
65
+ end
66
+ end
67
+
68
+ # Single file attachment stub
69
+ class ActiveStorageAttachmentSingle
70
+ attr_reader :url
71
+
72
+ def initialize(url)
73
+ @url = url
74
+ end
75
+
76
+ def attached?
77
+ !@url.nil?
78
+ end
79
+
80
+ def blank?
81
+ !attached?
82
+ end
83
+
84
+ def present?
85
+ attached?
86
+ end
87
+ end
88
+
89
+ # Multiple files attachment stub
90
+ class ActiveStorageAttachmentMultiple
91
+ include Enumerable
92
+
93
+ attr_reader :urls
94
+
95
+ def initialize(urls = [])
96
+ @urls = urls.compact
97
+ @attachments = @urls.map { |url| ActiveStorageAttachmentSingle.new(url) }
98
+ end
99
+
100
+ def attached?
101
+ @urls.any?
102
+ end
103
+
104
+ def blank?
105
+ !attached?
106
+ end
107
+
108
+ def present?
109
+ attached?
110
+ end
111
+
112
+ def each(&block)
113
+ @attachments.each(&block)
114
+ end
115
+
116
+ def map(&block)
117
+ @attachments.map(&block)
118
+ end
119
+
120
+ def size
121
+ @attachments.size
122
+ end
123
+
124
+ def count
125
+ size
126
+ end
127
+
128
+ def length
129
+ size
130
+ end
131
+
132
+ def empty?
133
+ @attachments.empty?
134
+ end
135
+
136
+ def first
137
+ @attachments.first
138
+ end
139
+
140
+ def last
141
+ @attachments.last
142
+ end
143
+
144
+ def [](index)
145
+ @attachments[index]
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Configure FactoryBot
4
+ RSpec.configure do |config|
5
+ # Include FactoryBot methods in RSpec
6
+ config.include FactoryBot::Syntax::Methods
7
+
8
+ # Lint factories before running tests (optional, comment out if too slow)
9
+ # config.before(:suite) do
10
+ # FactoryBot.lint
11
+ # end
12
+ end
13
+
14
+ # FactoryBot configuration
15
+ FactoryBot.define do
16
+ # Global configuration for FactoryBot
17
+
18
+ # Use sequences for unique values
19
+ sequence :email do |n|
20
+ "user#{n}@example.com"
21
+ end
22
+
23
+ sequence :uuid do
24
+ SecureRandom.uuid
25
+ end
26
+
27
+ # Add more global sequences or traits here as needed
28
+ end
29
+
30
+
31
+
32
+
33
+
34
+
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Configure Faker
4
+ Faker::Config.locale = 'en' # or 'id' for Indonesian
5
+
6
+ # Faker random seed for reproducible test data
7
+ Faker::Config.random = Random.new(42)
8
+
9
+ # Optional: Configure Faker to use unique values by default
10
+ # This prevents duplicate data in tests
11
+ # Faker::UniqueGenerator.clear # Call this in an after block if needed
12
+
13
+ # Common Faker helpers for use in factories
14
+ module FakerHelpers
15
+ def self.random_phone
16
+ Faker::PhoneNumber.phone_number
17
+ end
18
+
19
+ def self.random_address
20
+ Faker::Address.full_address
21
+ end
22
+
23
+ def self.random_company
24
+ Faker::Company.name
25
+ end
26
+
27
+ def self.random_sentence(word_count: 10)
28
+ Faker::Lorem.sentence(word_count: word_count)
29
+ end
30
+
31
+ def self.random_paragraph(sentence_count: 3)
32
+ Faker::Lorem.paragraph(sentence_count: sentence_count)
33
+ end
34
+
35
+ def self.random_url
36
+ Faker::Internet.url
37
+ end
38
+
39
+ def self.random_image_url(width: 640, height: 480)
40
+ Faker::LoremFlickr.image(size: "#{width}x#{height}")
41
+ end
42
+
43
+ def self.random_date(from: 1.year.ago, to: Date.today)
44
+ Faker::Date.between(from: from, to: to)
45
+ end
46
+
47
+ def self.random_datetime(from: 1.year.ago, to: Time.current)
48
+ Faker::Time.between(from: from, to: to)
49
+ end
50
+ end
51
+
52
+ # Make FakerHelpers available in tests
53
+ RSpec.configure do |config|
54
+ config.include FakerHelpers
55
+ end
56
+
57
+
58
+
59
+
60
+
61
+
data/lib/rider-kick.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'hashie'
4
- require 'rider_kick/entities/failure_details'
5
- require 'rider_kick/builders/abstract_active_record_entity_builder'
6
- require 'rider_kick/matchers/use_case_result'
7
- require 'rider_kick/use_cases/abstract_use_case'
8
- require 'rider_kick/use_cases/contract'
9
- require 'rider_kick/version'
4
+ require_relative 'rider_kick/entities/failure_details'
5
+ require_relative 'rider_kick/builders/abstract_active_record_entity_builder'
6
+ require_relative 'rider_kick/matchers/use_case_result'
7
+ require_relative 'rider_kick/use_cases/abstract_use_case'
8
+ require_relative 'rider_kick/use_cases/contract'
9
+ require_relative 'rider_kick/version'
10
+ require_relative 'generators/rider_kick/errors'
11
+ require_relative 'rider_kick/configuration'
10
12
 
11
13
  module RiderKick
12
14
  end