rails-domino 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f6dba679e8094d127d345f3e8cd68115192cdf5f701b447bbe9464d0e07d0dc4
4
+ data.tar.gz: 5041c268a9e5b2e7e45711bc12482802986b0dc20f02b4b55659dd0b95ce1156
5
+ SHA512:
6
+ metadata.gz: 8472903ef80712086e13011d7e8df94a1117264bc383d672bc9365ef5e96a16869cc82319276c6b75ba970958a52d0414a728e275912d55f44f82a362333a613
7
+ data.tar.gz: 2b2565ace0fdff4e6ad1bc45c9a4bae69958c11c3928980b9e6dde655467e5ad4e4a45c722882785bc6a6bec79b63e9f3d7ba6ec6b4d194982b63fb6b3d5b5ee
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abstract base repository for all models.
4
+ #
5
+ # Subclasses must implement standard CRUD methods.
6
+ class BaseRepository
7
+ def find_by_id(_id)
8
+ raise NotImplementedError, "Subclasses must implement find_by_id"
9
+ end
10
+
11
+ def all
12
+ raise NotImplementedError, "Subclasses must implement all"
13
+ end
14
+
15
+ def create(_attrs)
16
+ raise NotImplementedError, "Subclasses must implement create"
17
+ end
18
+
19
+ def update(_id, _attrs)
20
+ raise NotImplementedError, "Subclasses must implement update"
21
+ end
22
+
23
+ def delete(_id)
24
+ raise NotImplementedError, "Subclasses must implement delete"
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abstract base service for all domain logic.
4
+ #
5
+ # Subclasses must implement business operations.
6
+ class BaseService
7
+ def get(_id)
8
+ raise NotImplementedError, "Subclasses must implement get"
9
+ end
10
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ class DominoGenerator < Rails::Generators::NamedBase # rubocop:disable Style/Documentation
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ # Accept attributes for model
9
+ argument :attributes, type: :array, default: [], banner: "field[:type] field[:type]"
10
+
11
+ class_option :with_model, type: :boolean, default: false, desc: "Generate ActiveRecord model"
12
+
13
+ def create_model
14
+ return unless options[:with_model]
15
+
16
+ generate "model", "#{class_name} #{attributes.join(" ")}"
17
+ end
18
+
19
+ def create_service
20
+ template "service.rb.tt", File.join("app/services", "#{file_name}_service.rb")
21
+ end
22
+
23
+ def create_repository
24
+ template "repository.rb.tt", File.join("app/repositories", "#{file_name}_repository.rb")
25
+ end
26
+
27
+ def create_blueprint
28
+ template "blueprint.rb.tt", File.join("app/mappers", "#{file_name}_blueprint.rb")
29
+ end
30
+
31
+ def create_controller
32
+ template "controller.rb.tt", File.join("app/controllers", "#{file_name.pluralize}_controller.rb")
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ # Blueprint for serializing <%= class_name %> model.
2
+ #
3
+ # @see https://github.com/procore/blueprinter
4
+ #
5
+ class <%= class_name %>Blueprint < Blueprinter::Base
6
+ identifier :id
7
+
8
+ # Fields to expose
9
+ fields <%= attributes.map { |attr| ":#{attr.split(':').first}" }.join(', ') %>
10
+ end
@@ -0,0 +1,50 @@
1
+ # Controller for <%= class_name.pluralize %> API endpoints.
2
+ #
3
+ # Provides RESTful actions for <%= class_name %> resources.
4
+ #
5
+ class <%= class_name.pluralize %>Controller < ApplicationController
6
+ include Import['<%= file_name %>_service']
7
+
8
+ # GET /<%= file_name.pluralize %>
9
+ # @return [JSON]
10
+ def index
11
+ render json: <%= class_name %>Blueprint.render(@<%= file_name %>_service.all)
12
+ end
13
+
14
+ # GET /<%= file_name.pluralize %>/:id
15
+ # @param id [Integer]
16
+ # @return [JSON]
17
+ def show
18
+ render json: <%= class_name %>Blueprint.render(@<%= file_name %>_service.get(params[:id]))
19
+ end
20
+
21
+ # POST /<%= file_name.pluralize %>
22
+ # @return [JSON]
23
+ def create
24
+ obj = @<%= file_name %>_service.create(user_params)
25
+ render json: <%= class_name %>Blueprint.render(obj), status: :created
26
+ end
27
+
28
+ # PUT/PATCH /<%= file_name.pluralize %>/:id
29
+ # @param id [Integer]
30
+ # @return [JSON]
31
+ def update
32
+ obj = @<%= file_name %>_service.update(params[:id], user_params)
33
+ render json: <%= class_name %>Blueprint.render(obj)
34
+ end
35
+
36
+ # DELETE /<%= file_name %>/:id
37
+ # @param id [Integer]
38
+ # @return [JSON]
39
+ def destroy
40
+ success = @<%= file_name %>_service.delete(params[:id])
41
+ head(success ? :no_content : :not_found)
42
+ end
43
+
44
+ private
45
+
46
+ # @return [ActionController::Parameters]
47
+ def user_params
48
+ params.require(:<%= file_name %>).permit(<%= attributes.map { |a| ":#{a.split(':').first}" }.join(', ') %>)
49
+ end
50
+ end
@@ -0,0 +1,42 @@
1
+ # Repository for <%= class_name %> model.
2
+ #
3
+ # Provides abstracted access to ActiveRecord persistence.
4
+ #
5
+ # @example
6
+ # repo = <%= class_name %>Repository.new
7
+ # repo.find_by_id(1)
8
+ #
9
+ class <%= class_name %>Repository < BaseRepository
10
+ # @param id [Integer]
11
+ # @return [<%= class_name %>, nil]
12
+ def find_by_id(id)
13
+ <%= class_name %>.find_by(id: id)
14
+ end
15
+
16
+ # @return [Array<<%= class_name %>>]
17
+ def all
18
+ <%= class_name %>.all
19
+ end
20
+
21
+ # @param attrs [Hash]
22
+ # @return [<%= class_name %>]
23
+ def create(attrs)
24
+ <%= class_name %>.create(attrs)
25
+ end
26
+
27
+ # @param id [Integer]
28
+ # @param attrs [Hash]
29
+ # @return [<%= class_name %>, nil]
30
+ def update(id, attrs)
31
+ record = <%= class_name %>.find_by(id: id)
32
+ record.update(attrs) if record
33
+ record
34
+ end
35
+
36
+ # @param id [Integer]
37
+ # @return [<%= class_name %>, nil]
38
+ def delete(id)
39
+ record = <%= class_name %>.find_by(id: id)
40
+ record&.destroy
41
+ end
42
+ end
@@ -0,0 +1,46 @@
1
+ # Service for business logic around <%= class_name %>.
2
+ #
3
+ # Handles use-case orchestration and business operations.
4
+ #
5
+ # @example
6
+ # service = <%= class_name %>Service.new(...)
7
+ # service.get(1)
8
+ #
9
+ class <%= class_name %>Service < BaseService
10
+ include Import['<%= file_name %>_repository']
11
+
12
+ # @param <%= file_name %>_repository [<%= class_name %>Repository]
13
+ def initialize(<%= file_name %>_repository:)
14
+ @<%= file_name %>_repository = <%= file_name %>_repository
15
+ end
16
+
17
+ # @param id [Integer]
18
+ # @return [<%= class_name %>, nil]
19
+ def get(id)
20
+ @<%= file_name %>_repository.find_by_id(id)
21
+ end
22
+
23
+ # @param attrs [Hash]
24
+ # @return [<%= class_name %>]
25
+ def create(attrs)
26
+ @<%= file_name %>_repository.create(attrs)
27
+ end
28
+
29
+ # @param id [Integer]
30
+ # @param attrs [Hash]
31
+ # @return [<%= class_name %>, nil]
32
+ def update(id, attrs)
33
+ @<%= file_name %>_repository.update(id, attrs)
34
+ end
35
+
36
+ # @param id [Integer]
37
+ # @return [Boolean]
38
+ def delete(id)
39
+ !!@<%= file_name %>_repository.delete(id)
40
+ end
41
+
42
+ # @return [Array<<%= class_name %>>]
43
+ def all
44
+ @<%= file_name %>_repository.all
45
+ end
46
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Domino
4
+ # Scaffolder: handles the generation of model, repository, service, blueprint, and controller files
5
+ class Scaffolder
6
+ # Entrypoint: called from Domino.scaffold
7
+ #
8
+ # @param tables [Array<String>] optional list of tables to scaffold
9
+ # @param namespace [String, nil] reserved for future support
10
+ # @param generate_model [Boolean] whether to run model generation
11
+ def self.scaffold(tables: nil, namespace: nil, generate_model: true)
12
+ connection = ActiveRecord::Base.connection
13
+ tables ||= connection.tables.reject { |t| t == "schema_migrations" }
14
+
15
+ tables.each do |table|
16
+ klass = table.singularize.camelize
17
+ columns = connection.columns(table)
18
+ ScaffoldRunner.new(klass, columns, namespace, generate_model).run
19
+ end
20
+ end
21
+ end
22
+
23
+ # ScaffoldRunner: handles the actual file generation
24
+ class ScaffoldRunner
25
+ def initialize(model_name, columns, namespace, generate_model)
26
+ @model_name = model_name
27
+ @columns = columns
28
+ @namespace = namespace
29
+ @generate_model = generate_model
30
+ end
31
+
32
+ def run
33
+ generate_model_file if @generate_model
34
+ generate_file("repository")
35
+ generate_file("service")
36
+ generate_file("blueprint")
37
+ generate_file("controller")
38
+ end
39
+
40
+ def generate_model_file
41
+ system("rails generate model #{@model_name} #{column_args.join(" ")}")
42
+ end
43
+
44
+ def column_args
45
+ @columns.map { |col| "#{col.name}:#{col.sql_type}" unless col.name == "id" }.compact
46
+ end
47
+
48
+ def generate_file(type) # rubocop:disable Metrics/MethodLength
49
+ require "erb"
50
+
51
+ template_path = File.expand_path("../generators/domino/templates/#{type}.rb.tt", __FILE__)
52
+ raise "Missing template: #{template_path}" unless File.exist?(template_path)
53
+
54
+ content = ERB.new(File.read(template_path)).result(binding)
55
+
56
+ puts "Generating #{type} for #{@model_name}"
57
+
58
+ folder = case type
59
+ when "blueprint" then "app/mappers"
60
+ when "controller" then "app/controllers"
61
+ when "repository" then "app/repositories"
62
+ when "service" then "app/services"
63
+ else "app/#{type}s"
64
+ end
65
+
66
+ filename = case type
67
+ when "controller"
68
+ "#{@model_name.underscore.pluralize}_controller.rb"
69
+ else
70
+ "#{@model_name.underscore}_#{type}.rb"
71
+ end
72
+
73
+ FileUtils.mkdir_p(folder)
74
+ File.write(File.join(folder, filename), content)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Domino
4
+ VERSION = "0.1.0"
5
+ end
data/lib/domino.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "domino/version"
4
+ require "domino/scaffolder"
5
+
6
+ module Domino # rubocop:disable Style/Documentation
7
+ def self.scaffold(**kwargs)
8
+ Scaffolder.scaffold(**kwargs)
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-domino
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - kiebor81
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-07-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: blueprinter
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.30'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.30'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-auto_inject
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dry-container
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.9'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '6.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '6.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.0'
83
+ description:
84
+ email:
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - lib/domino.rb
90
+ - lib/domino/base_repository.rb
91
+ - lib/domino/base_service.rb
92
+ - lib/domino/generators/domino/domino_generator.rb
93
+ - lib/domino/generators/domino/templates/blueprint.rb.tt
94
+ - lib/domino/generators/domino/templates/controller.rb.tt
95
+ - lib/domino/generators/domino/templates/repository.rb.tt
96
+ - lib/domino/generators/domino/templates/service.rb.tt
97
+ - lib/domino/scaffolder.rb
98
+ - lib/domino/version.rb
99
+ homepage: https://github.com/kiebor81/rails-domino
100
+ licenses:
101
+ - MIT
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '3.0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubygems_version: 3.5.17
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Domain-first service and repository scaffold for Rails. Enforces DDD architecture
122
+ with Blueprinter and Dry-rb libraries
123
+ test_files: []