patternist 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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.devcontainer/Dockerfile +4 -0
  3. data/.devcontainer/devcontainer.json +23 -0
  4. data/.rspec +3 -0
  5. data/.rspec_status +48 -0
  6. data/.rubocop.yml +17 -0
  7. data/.ruby-lsp/.gitignore +1 -0
  8. data/.ruby-lsp/Gemfile +6 -0
  9. data/.ruby-lsp/Gemfile.lock +198 -0
  10. data/.ruby-lsp/last_updated +1 -0
  11. data/.ruby-lsp/main_lockfile_hash +1 -0
  12. data/CHANGELOG.md +5 -0
  13. data/LICENSE.txt +21 -0
  14. data/README.md +283 -0
  15. data/Rakefile +14 -0
  16. data/coverage/.last_run.json +5 -0
  17. data/coverage/.resultset.json +1041 -0
  18. data/coverage/.resultset.json.lock +0 -0
  19. data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc.png +0 -0
  20. data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
  21. data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_both.png +0 -0
  22. data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc.png +0 -0
  23. data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
  24. data/coverage/assets/0.13.1/application.css +1 -0
  25. data/coverage/assets/0.13.1/application.js +7 -0
  26. data/coverage/assets/0.13.1/colorbox/border.png +0 -0
  27. data/coverage/assets/0.13.1/colorbox/controls.png +0 -0
  28. data/coverage/assets/0.13.1/colorbox/loading.gif +0 -0
  29. data/coverage/assets/0.13.1/colorbox/loading_background.png +0 -0
  30. data/coverage/assets/0.13.1/favicon_green.png +0 -0
  31. data/coverage/assets/0.13.1/favicon_red.png +0 -0
  32. data/coverage/assets/0.13.1/favicon_yellow.png +0 -0
  33. data/coverage/assets/0.13.1/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  34. data/coverage/assets/0.13.1/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  35. data/coverage/assets/0.13.1/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  36. data/coverage/assets/0.13.1/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  37. data/coverage/assets/0.13.1/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  38. data/coverage/assets/0.13.1/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  39. data/coverage/assets/0.13.1/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  40. data/coverage/assets/0.13.1/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  41. data/coverage/assets/0.13.1/images/ui-icons_222222_256x240.png +0 -0
  42. data/coverage/assets/0.13.1/images/ui-icons_2e83ff_256x240.png +0 -0
  43. data/coverage/assets/0.13.1/images/ui-icons_454545_256x240.png +0 -0
  44. data/coverage/assets/0.13.1/images/ui-icons_888888_256x240.png +0 -0
  45. data/coverage/assets/0.13.1/images/ui-icons_cd0a0a_256x240.png +0 -0
  46. data/coverage/assets/0.13.1/loading.gif +0 -0
  47. data/coverage/assets/0.13.1/magnify.png +0 -0
  48. data/coverage/index.html +11400 -0
  49. data/docs/PERFORMANCE_ASSESSMENT.md +199 -0
  50. data/docs/benchmark.rb +222 -0
  51. data/docs/performance_improvements.rb +81 -0
  52. data/docs/performance_recommendations.rb +186 -0
  53. data/lib/patternist/controller.rb +12 -0
  54. data/lib/patternist/controllers/actionpack/helpers.rb +89 -0
  55. data/lib/patternist/controllers/actionpack/response_handling.rb +61 -0
  56. data/lib/patternist/controllers/actionpack/restful.rb +93 -0
  57. data/lib/patternist/version.rb +5 -0
  58. data/lib/patternist.rb +10 -0
  59. data/sig/patternist.rbs +4 -0
  60. metadata +125 -0
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string/inflections'
4
+
5
+ module Patternist
6
+ module Controllers
7
+ module ActionPack
8
+ # Provides helper methods for controller resource handling and naming conventions.
9
+ # Automatically infers resource classes and names based on controller naming.
10
+ module Helpers
11
+ CONTROLLER_SUFFIX = 'Controller'
12
+ NAMESPACE_SEPARATOR = '::'
13
+
14
+ def self.included(base)
15
+ base.extend ClassMethods
16
+ end
17
+
18
+ def collection = resource_class.all
19
+ def find_resource = resource_class.find(id_param)
20
+ def create_resource = resource.save
21
+ def update_resource = resource.update(resource_params)
22
+ def destroy_resource = resource.destroy
23
+ def resource_class = @resource_class ||= self.class.resource_class
24
+ def resource_name = @resource_name ||= self.class.resource_name
25
+ def resource_class_name = @resource_class_name ||= model_name_human || resource_class.name
26
+ def collection_name = @collection_name ||= resource_name.pluralize
27
+ def resource = instance_variable_get(instance_variable_name(resource_name))
28
+ def id_param = params.fetch(params_id_key, nil)
29
+ def params_id_key = :id
30
+
31
+ private
32
+
33
+ def model_name_human
34
+ resource_class.respond_to?(:model_name) && resource_class.model_name.human
35
+ end
36
+
37
+ def collection_instance=(value)
38
+ instance_variable_set(instance_variable_name(collection_name), value)
39
+ end
40
+
41
+ def resource_instance=(value)
42
+ instance_variable_set(instance_variable_name(resource_name), value)
43
+ end
44
+
45
+ def instance_variable_name(name)
46
+ :"@#{name}"
47
+ end
48
+
49
+ # Class methods automatically added to the including class
50
+ module ClassMethods
51
+ # Infers the resource class from the controller name
52
+ def resource_class
53
+ @resource_class ||= infer_resource_class
54
+ end
55
+
56
+ # Returns the underscored name of the resource class
57
+ def resource_name
58
+ @resource_name ||= resource_class.name.underscore
59
+ end
60
+
61
+ private
62
+
63
+ # Infers the resource class based on the controller name
64
+ def infer_resource_class
65
+ base_name = if name.end_with?(CONTROLLER_SUFFIX)
66
+ name[0...-CONTROLLER_SUFFIX.length]
67
+ else
68
+ name
69
+ end
70
+
71
+ last_separator = base_name.rindex(NAMESPACE_SEPARATOR)
72
+ controller_name = if last_separator
73
+ base_name[(last_separator + 2)..]
74
+ else
75
+ base_name
76
+ end
77
+
78
+ Object.const_get(controller_name.singularize) if controller_name
79
+ rescue NameError => e
80
+ raise NameError, "Could not infer resource class for #{name}: #{e.message}. " \
81
+ 'Please define `self.resource_class` in your controller.'
82
+ rescue StandardError => e
83
+ raise NameError, "An error occurred while inferring resource class for #{name}: #{e.message}."
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Patternist
4
+ module Controllers
5
+ module ActionPack
6
+ # Handles HTTP and JSON response formatting for controllers.
7
+ # Provides consistent response patterns across controllers with customizable handlers.
8
+ module ResponseHandling
9
+ HTML_FORMAT = :html
10
+ JSON_FORMAT = :json
11
+
12
+ def format_response(resource, notice:, status:, on_error_render:, formats: {}, &block)
13
+ respond_to do |format|
14
+ if block.call
15
+ handle_success(format, resource, notice, status, formats)
16
+ else
17
+ handle_error(format, resource, on_error_render, formats)
18
+ end
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ # Handles successful responses for different formats
25
+ def handle_success(format, resource, notice, status, custom_formats)
26
+ # TODO: DEPRECATE custom_formats?
27
+ dispatch_response(format, HTML_FORMAT, html_success(resource, notice: notice), custom_formats)
28
+ dispatch_response(format, JSON_FORMAT, json_success(resource, status: status), custom_formats)
29
+ end
30
+
31
+ # Handles error responses for different formats
32
+ def handle_error(format, resource, on_error_render, custom_formats)
33
+ dispatch_response(format, HTML_FORMAT, html_error(on_error_render), custom_formats, :on_error_html)
34
+ dispatch_response(format, JSON_FORMAT, json_error(resource), custom_formats, :on_error_json)
35
+ end
36
+
37
+ def html_success(location, notice:)
38
+ proc { redirect_to location, notice: notice }
39
+ end
40
+
41
+ def json_success(location, status:)
42
+ proc { render :show, status: status, location: location }
43
+ end
44
+
45
+ def html_error(on_error_render, status: :unprocessable_entity)
46
+ proc { render on_error_render, status: status }
47
+ end
48
+
49
+ def json_error(resource, status: :unprocessable_entity)
50
+ proc { render json: resource.errors, status: status }
51
+ end
52
+
53
+ def dispatch_response(format, format_method, default_proc, custom_procs, format_key = format_method)
54
+ format.public_send(format_method) do
55
+ (custom_procs[format_key] || default_proc).call
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
4
+ require_relative 'response_handling'
5
+
6
+ module Patternist
7
+ module Controllers
8
+ module ActionPack
9
+ # Provides RESTful CRUD actions for controllers.
10
+ # Requires implementing `resource_params` for strong parameter handling.
11
+ module Restful
12
+ def self.included(base)
13
+ base.include Helpers
14
+ base.include ResponseHandling
15
+ base.include InstanceMethods
16
+ end
17
+
18
+ # Instance methods implementing RESTful actions
19
+ module InstanceMethods
20
+ # Lists all resources
21
+ def index = self.collection_instance = collection
22
+
23
+ # Shows a single resource
24
+ def show
25
+ self.resource_instance = find_resource
26
+ yield if block_given?
27
+ end
28
+
29
+ # Prepares a resource for editing
30
+ def edit
31
+ self.resource_instance = find_resource
32
+ yield if block_given?
33
+ end
34
+
35
+ # Initializes a new resource
36
+ def new
37
+ self.resource_instance = resource_class.new
38
+ yield if block_given?
39
+ end
40
+
41
+ # Creates a new resource
42
+ def create
43
+ self.resource_instance = resource_class.new(resource_params)
44
+
45
+ format_response(resource,
46
+ notice: "#{resource_class_name} was successfully created.",
47
+ status: :created,
48
+ on_error_render: :new) do
49
+ create_resource
50
+ end
51
+ end
52
+
53
+ # Updates an existing resource
54
+ def update
55
+ self.resource_instance = find_resource
56
+
57
+ format_response(resource,
58
+ notice: "#{resource_class_name} was successfully updated.",
59
+ status: :ok,
60
+ on_error_render: :edit) do
61
+ update_resource
62
+ end
63
+ end
64
+
65
+ # Destroys an existing resource
66
+ def destroy
67
+ self.resource_instance = find_resource
68
+
69
+ notice = "#{resource_class_name} was successfully destroyed."
70
+ format_response(resource,
71
+ notice: notice,
72
+ status: :see_other,
73
+ on_error_render: :show,
74
+ formats: {
75
+ html: -> { redirect_to resource_class, notice: notice },
76
+ json: -> { head :no_content }
77
+ }) do
78
+ destroy_resource
79
+ end
80
+ end
81
+
82
+ protected
83
+
84
+ # Override this method to define allowed parameters
85
+ def resource_params
86
+ raise NotImplementedError,
87
+ 'Controller must define `resource_params`. Example: `params.require(:post).permit(:title, :body)`'
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Patternist
4
+ VERSION = '0.1.0'
5
+ end
data/lib/patternist.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'patternist/version'
4
+
5
+ module Patternist
6
+ class Error < StandardError; end
7
+ class NotImplementedError < Error; end
8
+ class NameError < Error; end
9
+ class FormatError < Error; end
10
+ end
@@ -0,0 +1,4 @@
1
+ module Patternist
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: patternist
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Emerson Xavier
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-07-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: actionpack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '9.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '9.0'
33
+ description: Reusable utilities for Ruby and Rails
34
+ email:
35
+ - msxavii@gmail.com
36
+ executables: []
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - ".devcontainer/Dockerfile"
41
+ - ".devcontainer/devcontainer.json"
42
+ - ".rspec"
43
+ - ".rspec_status"
44
+ - ".rubocop.yml"
45
+ - ".ruby-lsp/.gitignore"
46
+ - ".ruby-lsp/Gemfile"
47
+ - ".ruby-lsp/Gemfile.lock"
48
+ - ".ruby-lsp/last_updated"
49
+ - ".ruby-lsp/main_lockfile_hash"
50
+ - CHANGELOG.md
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - coverage/.last_run.json
55
+ - coverage/.resultset.json
56
+ - coverage/.resultset.json.lock
57
+ - coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc.png
58
+ - coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc_disabled.png
59
+ - coverage/assets/0.13.1/DataTables-1.10.20/images/sort_both.png
60
+ - coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc.png
61
+ - coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc_disabled.png
62
+ - coverage/assets/0.13.1/application.css
63
+ - coverage/assets/0.13.1/application.js
64
+ - coverage/assets/0.13.1/colorbox/border.png
65
+ - coverage/assets/0.13.1/colorbox/controls.png
66
+ - coverage/assets/0.13.1/colorbox/loading.gif
67
+ - coverage/assets/0.13.1/colorbox/loading_background.png
68
+ - coverage/assets/0.13.1/favicon_green.png
69
+ - coverage/assets/0.13.1/favicon_red.png
70
+ - coverage/assets/0.13.1/favicon_yellow.png
71
+ - coverage/assets/0.13.1/images/ui-bg_flat_0_aaaaaa_40x100.png
72
+ - coverage/assets/0.13.1/images/ui-bg_flat_75_ffffff_40x100.png
73
+ - coverage/assets/0.13.1/images/ui-bg_glass_55_fbf9ee_1x400.png
74
+ - coverage/assets/0.13.1/images/ui-bg_glass_65_ffffff_1x400.png
75
+ - coverage/assets/0.13.1/images/ui-bg_glass_75_dadada_1x400.png
76
+ - coverage/assets/0.13.1/images/ui-bg_glass_75_e6e6e6_1x400.png
77
+ - coverage/assets/0.13.1/images/ui-bg_glass_95_fef1ec_1x400.png
78
+ - coverage/assets/0.13.1/images/ui-bg_highlight-soft_75_cccccc_1x100.png
79
+ - coverage/assets/0.13.1/images/ui-icons_222222_256x240.png
80
+ - coverage/assets/0.13.1/images/ui-icons_2e83ff_256x240.png
81
+ - coverage/assets/0.13.1/images/ui-icons_454545_256x240.png
82
+ - coverage/assets/0.13.1/images/ui-icons_888888_256x240.png
83
+ - coverage/assets/0.13.1/images/ui-icons_cd0a0a_256x240.png
84
+ - coverage/assets/0.13.1/loading.gif
85
+ - coverage/assets/0.13.1/magnify.png
86
+ - coverage/index.html
87
+ - docs/PERFORMANCE_ASSESSMENT.md
88
+ - docs/benchmark.rb
89
+ - docs/performance_improvements.rb
90
+ - docs/performance_recommendations.rb
91
+ - lib/patternist.rb
92
+ - lib/patternist/controller.rb
93
+ - lib/patternist/controllers/actionpack/helpers.rb
94
+ - lib/patternist/controllers/actionpack/response_handling.rb
95
+ - lib/patternist/controllers/actionpack/restful.rb
96
+ - lib/patternist/version.rb
97
+ - sig/patternist.rbs
98
+ homepage: https://github.com/xavius-rb/patternist
99
+ licenses:
100
+ - MIT
101
+ metadata:
102
+ allowed_push_host: https://rubygems.org/
103
+ homepage_uri: https://github.com/xavius-rb/patternist
104
+ source_code_uri: https://github.com/xavius-rb/patternist
105
+ changelog_uri: https://github.com/xavius-rb/patternist/blob/main/CHANGELOG.md
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: 3.1.0
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubygems_version: 3.0.3.1
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Reusable utilities
125
+ test_files: []