rider-kick 0.0.15 → 0.0.16
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.
- checksums.yaml +4 -4
- data/lib/generators/rider_kick/clean_arch_generator.rb +1 -3
- data/lib/generators/rider_kick/scaffold_generator.rb +37 -37
- data/lib/generators/rider_kick/scaffold_generator_contracts_with_scope_spec.rb +1 -1
- data/lib/generators/rider_kick/scaffold_generator_list_spec_format_spec.rb +17 -11
- data/lib/generators/rider_kick/scaffold_generator_rspec_spec.rb +2 -11
- data/lib/generators/rider_kick/scaffold_generator_with_scope_spec.rb +1 -1
- data/lib/generators/rider_kick/templates/config/initializers/pagy.rb.tt +45 -10
- data/lib/generators/rider_kick/templates/db/structures/example.yaml.tt +1 -2
- data/lib/generators/rider_kick/templates/domains/core/builders/builder_spec.rb.tt +51 -38
- data/lib/generators/rider_kick/templates/domains/core/repositories/abstract_repository.rb.tt +7 -5
- data/lib/generators/rider_kick/templates/domains/core/repositories/create_spec.rb.tt +131 -40
- data/lib/generators/rider_kick/templates/domains/core/repositories/destroy.rb.tt +4 -5
- data/lib/generators/rider_kick/templates/domains/core/repositories/destroy_spec.rb.tt +102 -57
- data/lib/generators/rider_kick/templates/domains/core/repositories/fetch_by_id.rb.tt +1 -1
- data/lib/generators/rider_kick/templates/domains/core/repositories/fetch_by_id_spec.rb.tt +89 -37
- data/lib/generators/rider_kick/templates/domains/core/repositories/list.rb.tt +8 -3
- data/lib/generators/rider_kick/templates/domains/core/repositories/list_spec.rb.tt +141 -131
- data/lib/generators/rider_kick/templates/domains/core/repositories/update.rb.tt +2 -2
- data/lib/generators/rider_kick/templates/domains/core/repositories/update_spec.rb.tt +171 -80
- data/lib/generators/rider_kick/templates/domains/core/use_cases/create_spec.rb.tt +25 -5
- data/lib/generators/rider_kick/templates/domains/core/use_cases/destroy_spec.rb.tt +4 -4
- data/lib/generators/rider_kick/templates/domains/core/use_cases/fetch_by_id_spec.rb.tt +4 -4
- data/lib/generators/rider_kick/templates/domains/core/use_cases/list_spec.rb.tt +1 -1
- data/lib/generators/rider_kick/templates/domains/core/use_cases/update_spec.rb.tt +28 -8
- data/lib/rider_kick/matchers/use_case_result.rb +1 -1
- data/lib/rider_kick/use_cases/abstract_use_case_spec.rb +3 -3
- data/lib/rider_kick/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3c933ba32d84ebd605729faf5dddd9ee0392e2954b160c0e2ef0a23570f5c636
|
|
4
|
+
data.tar.gz: fab65284c253086df7acb0b40e89f078f20d436f241b63c3a0352e36d96127f2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e1dc64862b3707c35dd437dd9f2ce1af940d20fcf9f8c99f0160c65bc77c99f0aba9af9046a8ccc2ea8309ce56fff57cd6fada35afa46b996ff36f6462b645e5
|
|
7
|
+
data.tar.gz: 908f66d02665cac5897adb37cfc215a34cccee066bf009b111037a82f0a58ad905ac327d54e77bcbb0c67da65d8b3d75eae6aaa4479b65902e4581d6e1be61ff
|
|
@@ -221,6 +221,7 @@ module RiderKick
|
|
|
221
221
|
|
|
222
222
|
def gem_dependencies
|
|
223
223
|
<<~RUBY
|
|
224
|
+
|
|
224
225
|
group :development, :test do
|
|
225
226
|
gem "rspec-rails"
|
|
226
227
|
gem "factory_bot_rails"
|
|
@@ -241,9 +242,6 @@ module RiderKick
|
|
|
241
242
|
|
|
242
243
|
# pagination
|
|
243
244
|
gem 'pagy'
|
|
244
|
-
|
|
245
|
-
# models validation
|
|
246
|
-
gem 'schema_validations'
|
|
247
245
|
RUBY
|
|
248
246
|
end
|
|
249
247
|
|
|
@@ -93,11 +93,11 @@ module RiderKick
|
|
|
93
93
|
|
|
94
94
|
unless @model_class.column_names.include?(field_name)
|
|
95
95
|
raise ValidationError.new(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
"Repository filter error: Field '#{field_name}' tidak ditemukan di model #{@model_class}",
|
|
97
|
+
structure_file: "#{arg_structure}_structure.yaml",
|
|
98
|
+
field_name: field_name,
|
|
99
|
+
model_class: @model_class.to_s,
|
|
100
|
+
available_columns: @model_class.column_names
|
|
101
101
|
)
|
|
102
102
|
end
|
|
103
103
|
end
|
|
@@ -110,12 +110,12 @@ module RiderKick
|
|
|
110
110
|
|
|
111
111
|
if missing_fields.any?
|
|
112
112
|
raise ValidationError.new(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
"Entity configuration error: Field(s) tidak ditemukan di model #{@model_class}",
|
|
114
|
+
structure_file: "#{arg_structure}_structure.yaml",
|
|
115
|
+
missing_fields: missing_fields,
|
|
116
|
+
model_class: @model_class.to_s,
|
|
117
|
+
available_columns: @model_class.column_names,
|
|
118
|
+
suggestion: "Update section 'entity.db_attributes' di YAML file"
|
|
119
119
|
)
|
|
120
120
|
end
|
|
121
121
|
end
|
|
@@ -131,13 +131,13 @@ module RiderKick
|
|
|
131
131
|
def setup_structure_config
|
|
132
132
|
# Determine structure file path based on engine configuration
|
|
133
133
|
structure_path = if RiderKick.configuration.engine_name.present?
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
134
|
+
# For engines, read structure file from engine's db/structures directory
|
|
135
|
+
engine_name = RiderKick.configuration.engine_name.downcase
|
|
136
|
+
"engines/#{engine_name}/db/structures/#{arg_structure}_structure.yaml"
|
|
137
|
+
else
|
|
138
|
+
# For main app, read from host's db/structures directory
|
|
139
|
+
"db/structures/#{arg_structure}_structure.yaml"
|
|
140
|
+
end
|
|
141
141
|
|
|
142
142
|
validate_yaml_format!(structure_path)
|
|
143
143
|
config = YAML.load_file(structure_path)
|
|
@@ -200,15 +200,15 @@ module RiderKick
|
|
|
200
200
|
|
|
201
201
|
# Route scope
|
|
202
202
|
# @route_scope_path = arg_scope.fetch('scope', '').to_s.downcase
|
|
203
|
-
@route_scope_path = @structure.
|
|
203
|
+
@route_scope_path = @structure.scope.to_s.downcase
|
|
204
204
|
@route_scope_class = @route_scope_path.camelize
|
|
205
205
|
|
|
206
206
|
# Baca actor_id dari structure.yaml jika ada, jika tidak generate dari actor
|
|
207
207
|
@actor_id = if @structure.actor_id.present?
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
@structure.actor_id.to_s
|
|
209
|
+
elsif @actor.present?
|
|
210
|
+
"#{@actor.to_s.downcase}_id"
|
|
211
|
+
end
|
|
212
212
|
|
|
213
213
|
# Set flag untuk setiap action apakah resource_owner_id atau actor_id ada di contract
|
|
214
214
|
# Ini digunakan di template repository untuk conditional logic
|
|
@@ -231,19 +231,19 @@ module RiderKick
|
|
|
231
231
|
|
|
232
232
|
# Get contract untuk action tertentu
|
|
233
233
|
contract = case action.to_s
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
234
|
+
when 'list'
|
|
235
|
+
@contract_list
|
|
236
|
+
when 'fetch', 'fetch_by_id'
|
|
237
|
+
@contract_fetch_by_id
|
|
238
|
+
when 'create'
|
|
239
|
+
@contract_create
|
|
240
|
+
when 'update'
|
|
241
|
+
@contract_update
|
|
242
|
+
when 'destroy'
|
|
243
|
+
@contract_destroy
|
|
244
|
+
else
|
|
245
|
+
[]
|
|
246
|
+
end
|
|
247
247
|
contract ||= []
|
|
248
248
|
# Check apakah contract string mengandung field name
|
|
249
249
|
# Pattern: "required(:field_name)" atau "optional(:field_name)"
|
|
@@ -372,7 +372,7 @@ module RiderKick
|
|
|
372
372
|
|
|
373
373
|
def contract_fields
|
|
374
374
|
@model_class.columns.reject { |c| ['id', 'created_at', 'updated_at', 'type'].include?(c.name.to_s) }
|
|
375
|
-
|
|
375
|
+
.map { |c| c.name.to_s }
|
|
376
376
|
end
|
|
377
377
|
|
|
378
378
|
# --- AWAL BLOK MODIFIKASI: (PERBAIKAN KEGAGALAN #1) ---
|
|
@@ -70,7 +70,7 @@ RSpec.describe 'rider_kick:scaffold contracts (with scope)' do
|
|
|
70
70
|
|
|
71
71
|
klass.new(['users', 'scope:dashboard']).generate_use_case
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
RiderKick.configuration.domains_path
|
|
74
74
|
['owner_list_user', 'owner_fetch_user_by_id', 'owner_create_user', 'owner_update_user', 'owner_destroy_user'].each do |uc|
|
|
75
75
|
# expect(File).to exist("#{base}/#{uc}.rb")
|
|
76
76
|
end
|
|
@@ -47,10 +47,12 @@ RSpec.describe 'rider_kick:scaffold list spec generation' do
|
|
|
47
47
|
resource_name: article
|
|
48
48
|
actor: owner
|
|
49
49
|
resource_owner_id: account_id
|
|
50
|
+
resource_owner: account
|
|
50
51
|
domains:
|
|
51
52
|
action_list:
|
|
52
53
|
use_case:
|
|
53
|
-
contract:
|
|
54
|
+
contract:
|
|
55
|
+
- "required(:account_id).filled(:string)"
|
|
54
56
|
repository:
|
|
55
57
|
filters:
|
|
56
58
|
- "{ field: 'title', type: 'search' }"
|
|
@@ -73,13 +75,15 @@ RSpec.describe 'rider_kick:scaffold list spec generation' do
|
|
|
73
75
|
content = File.read(list_spec_file)
|
|
74
76
|
|
|
75
77
|
# Verify format matches user's requirements
|
|
76
|
-
expect(content).to include('
|
|
77
|
-
expect(content).to include('
|
|
78
|
-
expect(content).to include('
|
|
79
|
-
expect(content).to include('
|
|
80
|
-
expect(content).to include('
|
|
81
|
-
expect(content).to include('
|
|
82
|
-
expect(content).to include('context
|
|
78
|
+
expect(content).to include('require "rails_helper"')
|
|
79
|
+
expect(content).to include('subject(:result) { described_class.new(params: params).call }')
|
|
80
|
+
expect(content).to include('let(:account) { create(:account) }')
|
|
81
|
+
expect(content).to include('let(:account_id) { account.id }')
|
|
82
|
+
expect(content).to include('let!(:article_recent) { create(:article, account_id: account_id, created_at: 1.hour.ago) }')
|
|
83
|
+
expect(content).to include('let!(:other_account_article) { create(:article, account_id: other_account_id) }')
|
|
84
|
+
expect(content).to include('context "with valid params"')
|
|
85
|
+
expect(content).to include('context "with pagination"')
|
|
86
|
+
expect(content).to include('context "when account has no data"')
|
|
83
87
|
end
|
|
84
88
|
end
|
|
85
89
|
end
|
|
@@ -142,9 +146,11 @@ RSpec.describe 'rider_kick:scaffold list spec generation' do
|
|
|
142
146
|
content = File.read(list_spec_file)
|
|
143
147
|
|
|
144
148
|
# Verify format without resource_owner_id
|
|
145
|
-
expect(content).to include('
|
|
146
|
-
expect(content).to include('
|
|
147
|
-
expect(content).to include('
|
|
149
|
+
expect(content).to include('require "rails_helper"')
|
|
150
|
+
expect(content).to include('subject(:result) { described_class.new(params: params).call }')
|
|
151
|
+
expect(content).to include('let!(:product_recent) { create(:product, created_at: 1.hour.ago) }')
|
|
152
|
+
expect(content).to include('context "when no resources exist"')
|
|
153
|
+
expect(content).to include('Models::Product.destroy_all')
|
|
148
154
|
expect(content).not_to include('account_id')
|
|
149
155
|
expect(content).not_to include('user_id')
|
|
150
156
|
end
|
|
@@ -131,23 +131,14 @@ RSpec.describe 'rider_kick:scaffold generator (RSpec generation)' do
|
|
|
131
131
|
|
|
132
132
|
# Verifikasi konten spec file
|
|
133
133
|
spec_content = File.read(spec_file)
|
|
134
|
-
expect(spec_content).to include('require
|
|
134
|
+
expect(spec_content).to include('require "rails_helper"')
|
|
135
135
|
expect(spec_content).to include('RSpec.describe')
|
|
136
|
-
expect(spec_content).to include('describe
|
|
136
|
+
expect(spec_content).to include('describe "#call"')
|
|
137
137
|
|
|
138
138
|
# Only check Hashie::Mash for create, update, destroy, fetch_by_id (list uses simple hash)
|
|
139
139
|
if ['create_product', 'update_product', 'destroy_product', 'fetch_product_by_id'].include?(repo)
|
|
140
140
|
expect(spec_content).to include('Hashie::Mash.new')
|
|
141
141
|
end
|
|
142
|
-
|
|
143
|
-
# Verify error mocking for create, update, destroy (but not list or fetch_by_id)
|
|
144
|
-
if ['create_product', 'update_product', 'destroy_product'].include?(repo)
|
|
145
|
-
expect(spec_content).to include('let(:error_messages)')
|
|
146
|
-
expect(spec_content).to include('let(:active_model_errors)')
|
|
147
|
-
expect(spec_content).to include('allow(errors).to receive(:each)')
|
|
148
|
-
expect(spec_content).to include('double(as_json:')
|
|
149
|
-
expect(spec_content).to include("'options' => { 'message' => 'must be valid format' }")
|
|
150
|
-
end
|
|
151
142
|
end
|
|
152
143
|
|
|
153
144
|
# Builder spec (covers entity validation too)
|
|
@@ -62,7 +62,7 @@ RSpec.describe 'rider_kick:scaffold generator (with scope)' do
|
|
|
62
62
|
instance.generate_use_case
|
|
63
63
|
|
|
64
64
|
# use_cases berada di app/domains/use_cases/dashboard/users/...
|
|
65
|
-
|
|
65
|
+
RiderKick.configuration.domains_path
|
|
66
66
|
['owner_create_user', 'owner_update_user', 'owner_list_user', 'owner_destroy_user', 'owner_fetch_user_by_id'].each do |uc|
|
|
67
67
|
# expect(File).to exist(File.join(path, "#{uc}.rb"))
|
|
68
68
|
end
|
|
@@ -1,13 +1,48 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
Pagy::DEFAULT[:size] = 9 # nav bar links
|
|
7
|
-
# Better user experience handled automatically
|
|
8
|
-
require 'pagy/extras/overflow'
|
|
9
|
-
Pagy::DEFAULT[:overflow] = :empty_page
|
|
10
|
-
require 'pagy/extras/elasticsearch_rails'
|
|
11
|
-
require 'pagy/extras/array'
|
|
3
|
+
# Pagy initializer file (43.2.4)
|
|
4
|
+
# See https://ddnexus.github.io/pagy/resources/initializer/
|
|
12
5
|
|
|
13
|
-
|
|
6
|
+
############ Global Options ################################################################
|
|
7
|
+
# See https://ddnexus.github.io/pagy/toolbox/options/ for details.
|
|
8
|
+
# Add your global options below. They will be applied globally.
|
|
9
|
+
# For example:
|
|
10
|
+
#
|
|
11
|
+
Pagy.options[:limit] = 10 # Limit the items per page
|
|
12
|
+
Pagy.options[:client_max_limit] = 50 # The client can request a limit up to 100
|
|
13
|
+
Pagy.options[:max_pages] = 200 # Allow only 200 pages
|
|
14
|
+
# Pagy.options[:jsonapi] = true # Use JSON:API compliant URLs
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
############ JavaScript ####################################################################
|
|
18
|
+
# See https://ddnexus.github.io/pagy/resources/javascript/ for details.
|
|
19
|
+
# Examples for Rails:
|
|
20
|
+
# For apps with an assets pipeline
|
|
21
|
+
# Rails.application.config.assets.paths << Pagy::ROOT.join('javascripts')
|
|
22
|
+
#
|
|
23
|
+
# For apps with a javascript builder (e.g. esbuild, webpack, etc.)
|
|
24
|
+
# javascript_dir = Rails.root.join('app/javascript')
|
|
25
|
+
# Pagy.sync_javascript(javascript_dir, 'pagy.mjs') if Rails.env.development?
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
############# Overriding Pagy::I18n Lookup #################################################
|
|
29
|
+
# Refer to https://ddnexus.github.io/pagy/resources/i18n/ for details.
|
|
30
|
+
# Override the I18n lookup by dropping your custom dictionary in some pagy dir.
|
|
31
|
+
# Example for Rails:
|
|
32
|
+
#
|
|
33
|
+
# Pagy::I18n.pathnames << Rails.root.join('config/locales/pagy')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
############# I18n Gem Translation #########################################################
|
|
37
|
+
# See https://ddnexus.github.io/pagy/resources/i18n/ for details.
|
|
38
|
+
#
|
|
39
|
+
# Pagy.translate_with_the_slower_i18n_gem!
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
############# Calendar Localization for non-en locales ####################################
|
|
43
|
+
# See https://ddnexus.github.io/pagy/toolbox/paginators/calendar#localization for details.
|
|
44
|
+
# Add your desired locales to the list and uncomment the following line to enable them,
|
|
45
|
+
# regardless of whether you use the I18n gem for translations or not, whether with
|
|
46
|
+
# Rails or not.
|
|
47
|
+
#
|
|
48
|
+
# Pagy::Calendar.localize_with_rails_i18n_gem(*your_locales)
|
|
@@ -8,8 +8,7 @@ resource_owner_id: <%= @resource_owner_id %> # account_id
|
|
|
8
8
|
resource_owner: <%= @resource_owner %> # account
|
|
9
9
|
actor: <%= @actor %> # user
|
|
10
10
|
actor_id: <%= @actor_id %> # user_id
|
|
11
|
-
scope: '' #dashboard
|
|
12
|
-
domain: '' # core
|
|
11
|
+
scope: '' # dashboard
|
|
13
12
|
|
|
14
13
|
fields:
|
|
15
14
|
<% @fields.each do |f| -%>
|
|
@@ -6,30 +6,41 @@ RSpec.describe <%= domain_class_name %>::Builders::<%= @subject_class %>, type:
|
|
|
6
6
|
describe '#build' do
|
|
7
7
|
let(:<%= @variable_subject %>) do
|
|
8
8
|
ClassStubber::Model.new(
|
|
9
|
-
'id' =>
|
|
10
|
-
<%
|
|
9
|
+
'id' => SecureRandom.uuid,
|
|
10
|
+
<%
|
|
11
11
|
# Get uploader field names to exclude from entity_db_fields
|
|
12
12
|
uploader_names = @uploaders.map { |u| u.name.to_s }
|
|
13
13
|
-%>
|
|
14
14
|
<% @entity_db_fields.each do |field| -%>
|
|
15
15
|
<% next if uploader_names.include?(field.to_s) -%>
|
|
16
16
|
<% column_meta = get_column_meta(field) -%>
|
|
17
|
-
<%
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
17
|
+
<%
|
|
18
|
+
faker_val = case field.to_s
|
|
19
|
+
when /email/ then "Faker::Internet.email"
|
|
20
|
+
when /name/ then "Faker::Name.name"
|
|
21
|
+
when /first_name/ then "Faker::Name.first_name"
|
|
22
|
+
when /last_name/ then "Faker::Name.last_name"
|
|
23
|
+
when /title/ then "Faker::Book.title"
|
|
24
|
+
when /description/ then "Faker::Lorem.paragraph"
|
|
25
|
+
when /address/ then "Faker::Address.full_address"
|
|
26
|
+
when /phone/ then "Faker::PhoneNumber.cell_phone"
|
|
27
|
+
when /url/, /website/ then "Faker::Internet.url"
|
|
28
|
+
when /price/ then "Faker::Commerce.price"
|
|
29
|
+
else
|
|
30
|
+
case column_meta[:type].to_s
|
|
31
|
+
when 'datetime', 'timestamp' then "Faker::Time.backward(days: 14)"
|
|
32
|
+
when 'date' then "Faker::Date.backward(days: 14)"
|
|
33
|
+
when 'time' then "Faker::Time.backward(days: 14)"
|
|
34
|
+
when 'boolean' then "Faker::Boolean.boolean"
|
|
35
|
+
when 'integer', 'bigint' then "Faker::Number.number(digits: 2)"
|
|
36
|
+
when 'float' then "Faker::Number.decimal(l_digits: 2, r_digits: 2).to_f"
|
|
37
|
+
when 'decimal' then "Faker::Number.decimal(l_digits: 2, r_digits: 2).to_d"
|
|
38
|
+
when 'uuid' then "SecureRandom.uuid"
|
|
39
|
+
else "Faker::Lorem.word"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
-%>
|
|
43
|
+
'<%= field %>' => <%= faker_val %>,
|
|
33
44
|
<% end -%>
|
|
34
45
|
<% @uploaders.each do |uploader| -%>
|
|
35
46
|
<% if uploader.type == 'single' -%>
|
|
@@ -52,7 +63,7 @@ uploader_names = @uploaders.map { |u| u.name.to_s }
|
|
|
52
63
|
entity = builder.build
|
|
53
64
|
|
|
54
65
|
expect(entity).to be_a(<%= domain_class_name %>::Entities::<%= @subject_class %>)
|
|
55
|
-
expect(entity.id).to
|
|
66
|
+
expect(entity.id).to be_a(String)
|
|
56
67
|
<% @entity_db_fields.first(2).each do |field| -%>
|
|
57
68
|
<% column_meta = get_column_meta(field) -%>
|
|
58
69
|
<% case column_meta[:type].to_s -%>
|
|
@@ -61,20 +72,22 @@ uploader_names = @uploaders.map { |u| u.name.to_s }
|
|
|
61
72
|
<% when 'date' -%>
|
|
62
73
|
expect(entity.<%= field %>).to be_a(Date)
|
|
63
74
|
<% when 'boolean' -%>
|
|
64
|
-
expect(entity.<%= field %>)
|
|
75
|
+
expect([true, false]).to include(entity.<%= field %>)
|
|
65
76
|
<% when 'integer', 'bigint' -%>
|
|
66
|
-
expect(entity.<%= field %>).to
|
|
77
|
+
expect(entity.<%= field %>).to be_a(Integer)
|
|
67
78
|
<% when 'decimal', 'float' -%>
|
|
68
|
-
expect(entity.<%= field %>).to
|
|
79
|
+
expect(entity.<%= field %>).to be_a(Numeric)
|
|
80
|
+
<% when 'uuid' -%>
|
|
81
|
+
expect(entity.<%= field %>).to be_a(String)
|
|
69
82
|
<% else -%>
|
|
70
|
-
expect(entity.<%= field %>).to
|
|
83
|
+
expect(entity.<%= field %>).to be_a(String)
|
|
71
84
|
<% end -%>
|
|
72
85
|
<% end -%>
|
|
73
86
|
end
|
|
74
87
|
|
|
75
88
|
it 'includes all required entity attributes', :aggregate_failures do
|
|
76
89
|
entity = builder.build
|
|
77
|
-
|
|
90
|
+
|
|
78
91
|
# Entity must have these attributes (keys must exist, values can be nil for optional)
|
|
79
92
|
expect(entity).to respond_to(:id)
|
|
80
93
|
<% @entity_db_fields.each do |field| -%>
|
|
@@ -96,7 +109,7 @@ uploader_names = @uploaders.map { |u| u.name.to_s }
|
|
|
96
109
|
describe '#attributes_for_entity' do
|
|
97
110
|
it 'returns hash with uploader attributes', :aggregate_failures do
|
|
98
111
|
attributes = builder.send(:attributes_for_entity)
|
|
99
|
-
|
|
112
|
+
|
|
100
113
|
expect(attributes).to be_a(Hash)
|
|
101
114
|
<% @uploaders.each do |uploader| -%>
|
|
102
115
|
<% if uploader.type == 'single' -%>
|
|
@@ -109,7 +122,7 @@ uploader_names = @uploaders.map { |u| u.name.to_s }
|
|
|
109
122
|
|
|
110
123
|
it 'generates correct URL attributes', :aggregate_failures do
|
|
111
124
|
attributes = builder.send(:attributes_for_entity)
|
|
112
|
-
|
|
125
|
+
|
|
113
126
|
<% @uploaders.each do |uploader| -%>
|
|
114
127
|
<% if uploader.type == 'single' -%>
|
|
115
128
|
expect(attributes[:<%= uploader.name %>_url]).to eq('http://example.com/<%= uploader.name %>.jpg')
|
|
@@ -125,14 +138,14 @@ uploader_names = @uploaders.map { |u| u.name.to_s }
|
|
|
125
138
|
it 'ensures all entity attributes have correct keys and types', :aggregate_failures do
|
|
126
139
|
entity = builder.build
|
|
127
140
|
entity_hash = entity.to_h
|
|
128
|
-
|
|
141
|
+
|
|
129
142
|
# All entity attributes must have keys in the hash (source of truth from entity)
|
|
130
143
|
expect(entity_hash).to have_key(:id)
|
|
131
144
|
expect(entity.id).to be_a(String)
|
|
132
145
|
<% @entity_db_fields.each do |field| -%>
|
|
133
146
|
<% next if uploader_names.include?(field.to_s) -%>
|
|
134
147
|
<% column_meta = get_column_meta(field) -%>
|
|
135
|
-
|
|
148
|
+
|
|
136
149
|
expect(entity_hash).to have_key(:<%= field %>)
|
|
137
150
|
<% case column_meta[:type].to_s -%>
|
|
138
151
|
<% when 'datetime', 'timestamp', 'time' -%>
|
|
@@ -151,19 +164,19 @@ uploader_names = @uploaders.map { |u| u.name.to_s }
|
|
|
151
164
|
<% end -%>
|
|
152
165
|
<% @uploaders.each do |uploader| -%>
|
|
153
166
|
<% if uploader.type == 'single' -%>
|
|
154
|
-
|
|
167
|
+
|
|
155
168
|
expect(entity_hash).to have_key(:<%= uploader.name %>_url)
|
|
156
169
|
expect(entity.<%= uploader.name %>_url).to be_a(String).or be_nil
|
|
157
170
|
<% else -%>
|
|
158
|
-
|
|
171
|
+
|
|
159
172
|
expect(entity_hash).to have_key(:<%= uploader.name %>_urls)
|
|
160
173
|
expect(entity.<%= uploader.name %>_urls).to be_a(Array)
|
|
161
174
|
<% end -%>
|
|
162
175
|
<% end -%>
|
|
163
|
-
|
|
176
|
+
|
|
164
177
|
expect(entity_hash).to have_key(:created_at)
|
|
165
178
|
expect(entity.created_at).to be_a(Time) if entity.created_at.present?
|
|
166
|
-
|
|
179
|
+
|
|
167
180
|
expect(entity_hash).to have_key(:updated_at)
|
|
168
181
|
expect(entity.updated_at).to be_a(Time) if entity.updated_at.present?
|
|
169
182
|
end
|
|
@@ -171,7 +184,7 @@ uploader_names = @uploaders.map { |u| u.name.to_s }
|
|
|
171
184
|
it 'validates entity type schema definitions', :aggregate_failures do
|
|
172
185
|
# Verify entity has correct Dry::Types definitions
|
|
173
186
|
schema = <%= domain_class_name %>::Entities::<%= @subject_class %>.schema
|
|
174
|
-
|
|
187
|
+
|
|
175
188
|
# ID must be required (not optional)
|
|
176
189
|
id_key = schema.key(:id)
|
|
177
190
|
expect(id_key.required?).to be true
|
|
@@ -180,7 +193,7 @@ uploader_names = @uploaders.map { |u| u.name.to_s }
|
|
|
180
193
|
<% @entity_db_fields.each do |field| -%>
|
|
181
194
|
<% next if uploader_names.include?(field.to_s) -%>
|
|
182
195
|
<% column_meta = get_column_meta(field) -%>
|
|
183
|
-
|
|
196
|
+
|
|
184
197
|
# <%= field %>: <%= column_meta[:null] ? 'optional' : 'required' %>
|
|
185
198
|
<% if column_meta[:null] -%>
|
|
186
199
|
# Optional field - can be nil
|
|
@@ -195,22 +208,22 @@ uploader_names = @uploaders.map { |u| u.name.to_s }
|
|
|
195
208
|
<% end -%>
|
|
196
209
|
<% @uploaders.each do |uploader| -%>
|
|
197
210
|
<% if uploader.type == 'single' -%>
|
|
198
|
-
|
|
211
|
+
|
|
199
212
|
# <%= uploader.name %>_url: optional (uploader can be nil)
|
|
200
213
|
<%= uploader.name %>_url_key = schema.key(:<%= uploader.name %>_url)
|
|
201
214
|
expect(<%= uploader.name %>_url_key.required?).to be false
|
|
202
215
|
<% else -%>
|
|
203
|
-
|
|
216
|
+
|
|
204
217
|
# <%= uploader.name %>_urls: required (but can be empty array)
|
|
205
218
|
<%= uploader.name %>_urls_key = schema.key(:<%= uploader.name %>_urls)
|
|
206
219
|
expect(<%= uploader.name %>_urls_key.required?).to be true
|
|
207
220
|
<% end -%>
|
|
208
221
|
<% end -%>
|
|
209
|
-
|
|
222
|
+
|
|
210
223
|
# Timestamps - check their actual definition
|
|
211
224
|
created_at_key = schema.key(:created_at)
|
|
212
225
|
updated_at_key = schema.key(:updated_at)
|
|
213
|
-
|
|
226
|
+
|
|
214
227
|
# Verify timestamps exist (they might be optional in some entities)
|
|
215
228
|
expect(schema.keys.map(&:name)).to include(:created_at, :updated_at)
|
|
216
229
|
end
|
data/lib/generators/rider_kick/templates/domains/core/repositories/abstract_repository.rb.tt
CHANGED
|
@@ -11,12 +11,14 @@ class <%= domain_class_name %>::Repositories::AbstractRepository
|
|
|
11
11
|
record.errors.to_a.join(', ')
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
def build_error(code, message, attribute: nil)
|
|
15
|
+
[{ code: code, message: message, attribute: attribute }]
|
|
16
|
+
end
|
|
17
|
+
|
|
14
18
|
def build_errors(resource)
|
|
15
|
-
errors
|
|
16
|
-
|
|
17
|
-
errors << <%= domain_class_name %>::Builders::Error.new(error.as_json).build
|
|
19
|
+
resource.errors.map do |error|
|
|
20
|
+
{ code: error.type.to_s, message: error.message, attribute: error.attribute.to_s }
|
|
18
21
|
end
|
|
19
|
-
errors
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
def prepare!(params, sanitize: true)
|
|
@@ -33,7 +35,7 @@ class <%= domain_class_name %>::Repositories::AbstractRepository
|
|
|
33
35
|
|
|
34
36
|
def build_pagination_variable!(params)
|
|
35
37
|
@per_page = params[:per_page] || Pagy::DEFAULT[:limit]
|
|
36
|
-
@page = params[:page] ||
|
|
38
|
+
@page = params[:page] || 1
|
|
37
39
|
@search = params[:search]
|
|
38
40
|
end
|
|
39
41
|
end
|