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.
- checksums.yaml +7 -0
- data/.devcontainer/Dockerfile +4 -0
- data/.devcontainer/devcontainer.json +23 -0
- data/.rspec +3 -0
- data/.rspec_status +48 -0
- data/.rubocop.yml +17 -0
- data/.ruby-lsp/.gitignore +1 -0
- data/.ruby-lsp/Gemfile +6 -0
- data/.ruby-lsp/Gemfile.lock +198 -0
- data/.ruby-lsp/last_updated +1 -0
- data/.ruby-lsp/main_lockfile_hash +1 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +283 -0
- data/Rakefile +14 -0
- data/coverage/.last_run.json +5 -0
- data/coverage/.resultset.json +1041 -0
- data/coverage/.resultset.json.lock +0 -0
- data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.13.1/application.css +1 -0
- data/coverage/assets/0.13.1/application.js +7 -0
- data/coverage/assets/0.13.1/colorbox/border.png +0 -0
- data/coverage/assets/0.13.1/colorbox/controls.png +0 -0
- data/coverage/assets/0.13.1/colorbox/loading.gif +0 -0
- data/coverage/assets/0.13.1/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.13.1/favicon_green.png +0 -0
- data/coverage/assets/0.13.1/favicon_red.png +0 -0
- data/coverage/assets/0.13.1/favicon_yellow.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.13.1/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.13.1/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.13.1/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.13.1/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.13.1/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.13.1/loading.gif +0 -0
- data/coverage/assets/0.13.1/magnify.png +0 -0
- data/coverage/index.html +11400 -0
- data/docs/PERFORMANCE_ASSESSMENT.md +199 -0
- data/docs/benchmark.rb +222 -0
- data/docs/performance_improvements.rb +81 -0
- data/docs/performance_recommendations.rb +186 -0
- data/lib/patternist/controller.rb +12 -0
- data/lib/patternist/controllers/actionpack/helpers.rb +89 -0
- data/lib/patternist/controllers/actionpack/response_handling.rb +61 -0
- data/lib/patternist/controllers/actionpack/restful.rb +93 -0
- data/lib/patternist/version.rb +5 -0
- data/lib/patternist.rb +10 -0
- data/sig/patternist.rbs +4 -0
- metadata +125 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Patternist Performance Assessment Report
|
|
2
|
+
# Generated: July 5, 2025
|
|
3
|
+
|
|
4
|
+
## Executive Summary
|
|
5
|
+
|
|
6
|
+
After analyzing the `Patternist::Controller` and `Controllers::ActionPack::Restful` modules, I've identified several performance characteristics and optimization opportunities. The modules demonstrate good caching practices but have room for improvement in string handling and memory allocation.
|
|
7
|
+
|
|
8
|
+
## Current Performance Metrics
|
|
9
|
+
|
|
10
|
+
Based on benchmarking 1000 operations each:
|
|
11
|
+
|
|
12
|
+
| Operation | Time (seconds) | Performance Rating |
|
|
13
|
+
|-----------|----------------|-------------------|
|
|
14
|
+
| Controller creation | 0.000778 | ✅ Excellent |
|
|
15
|
+
| resource_class access | 0.001571 | ⚠️ Could improve |
|
|
16
|
+
| resource_name access | 0.000056 | ✅ Excellent |
|
|
17
|
+
| collection_name access | 0.000057 | ✅ Excellent |
|
|
18
|
+
|
|
19
|
+
**Key Findings:**
|
|
20
|
+
- `resource_class` access is ~28x slower than other cached operations
|
|
21
|
+
- String operations are well-optimized through memoization
|
|
22
|
+
- Controller instantiation is performant
|
|
23
|
+
- Memory allocation patterns follow Ruby best practices
|
|
24
|
+
|
|
25
|
+
## Detailed Analysis
|
|
26
|
+
|
|
27
|
+
### 🟢 Strengths
|
|
28
|
+
|
|
29
|
+
1. **Effective Memoization Strategy**
|
|
30
|
+
```ruby
|
|
31
|
+
@resource_class ||= self.class.resource_class
|
|
32
|
+
@resource_name ||= self.class.resource_name
|
|
33
|
+
```
|
|
34
|
+
- Prevents repeated expensive computations
|
|
35
|
+
- Proper use of `||=` operator
|
|
36
|
+
|
|
37
|
+
2. **Frozen String Literals**
|
|
38
|
+
```ruby
|
|
39
|
+
# frozen_string_literal: true
|
|
40
|
+
```
|
|
41
|
+
- Reduces string object allocations
|
|
42
|
+
- Improves memory efficiency
|
|
43
|
+
|
|
44
|
+
3. **Modular Architecture**
|
|
45
|
+
- Clean separation of concerns
|
|
46
|
+
- Each module has a focused responsibility
|
|
47
|
+
- Easy to test and maintain
|
|
48
|
+
|
|
49
|
+
### 🟡 Areas for Improvement
|
|
50
|
+
|
|
51
|
+
#### 1. String Operations in Class Inference (HIGH PRIORITY)
|
|
52
|
+
|
|
53
|
+
**Current Implementation:**
|
|
54
|
+
```ruby
|
|
55
|
+
def infer_resource_class
|
|
56
|
+
controller_name = name.gsub(/Controller$/, '').split('::').last
|
|
57
|
+
return Object.const_get(controller_name.singularize) if controller_name
|
|
58
|
+
end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Issues:**
|
|
62
|
+
- Multiple string allocations with `gsub` and `split`
|
|
63
|
+
- Regex compilation on every call
|
|
64
|
+
- Temporary array creation
|
|
65
|
+
|
|
66
|
+
**Recommended Optimization:**
|
|
67
|
+
```ruby
|
|
68
|
+
CONTROLLER_SUFFIX = 'Controller'
|
|
69
|
+
NAMESPACE_SEPARATOR = '::'
|
|
70
|
+
|
|
71
|
+
def infer_resource_class
|
|
72
|
+
base_name = name.end_with?(CONTROLLER_SUFFIX) ?
|
|
73
|
+
name[0...-CONTROLLER_SUFFIX.length] : name
|
|
74
|
+
|
|
75
|
+
last_separator = base_name.rindex(NAMESPACE_SEPARATOR)
|
|
76
|
+
controller_name = last_separator ?
|
|
77
|
+
base_name[(last_separator + 2)..-1] : base_name
|
|
78
|
+
|
|
79
|
+
Object.const_get(controller_name.singularize) if controller_name
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Expected Improvement:** 30-50% faster string processing
|
|
84
|
+
|
|
85
|
+
#### 2. Response Handler Memory Allocation (MEDIUM PRIORITY)
|
|
86
|
+
|
|
87
|
+
**Current Implementation:**
|
|
88
|
+
```ruby
|
|
89
|
+
def format_response(resource, formats: {}, &block)
|
|
90
|
+
respond_to do |format|
|
|
91
|
+
if block.call
|
|
92
|
+
handle_format(format, formats, :html, -> { redirect_to resource, notice: notice })
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Issues:**
|
|
99
|
+
- Lambda objects created on every call
|
|
100
|
+
- Memory allocation in hot path
|
|
101
|
+
|
|
102
|
+
**Recommended Optimization:**
|
|
103
|
+
```ruby
|
|
104
|
+
class ResponseHandlers
|
|
105
|
+
def self.html_redirect(resource, notice)
|
|
106
|
+
proc { redirect_to resource, notice: notice }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### 3. Instance Variable Name Generation (LOW PRIORITY)
|
|
112
|
+
|
|
113
|
+
**Current Implementation:**
|
|
114
|
+
```ruby
|
|
115
|
+
def instance_variable_name(name)
|
|
116
|
+
"@#{name}"
|
|
117
|
+
end
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Optimized Version:**
|
|
121
|
+
```ruby
|
|
122
|
+
def instance_variable_name(name)
|
|
123
|
+
:"@#{name}" # Use symbol for better performance
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Memory Usage Analysis
|
|
128
|
+
|
|
129
|
+
### Allocation Patterns
|
|
130
|
+
|
|
131
|
+
1. **Good:** Effective use of memoization reduces repeated allocations
|
|
132
|
+
2. **Concern:** String concatenation in error messages
|
|
133
|
+
3. **Opportunity:** Lambda allocation in response handling
|
|
134
|
+
|
|
135
|
+
### Recommended Memory Optimizations
|
|
136
|
+
|
|
137
|
+
1. **Use Frozen Constants:**
|
|
138
|
+
```ruby
|
|
139
|
+
ERROR_TEMPLATE = "Could not infer resource class for %s".freeze
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
2. **Pre-allocate Common Objects:**
|
|
143
|
+
```ruby
|
|
144
|
+
module ResponseFormats
|
|
145
|
+
HTML_SUCCESS = proc { |resource, notice| redirect_to resource, notice: notice }
|
|
146
|
+
JSON_SUCCESS = proc { |resource, status| render :show, status: status, location: resource }
|
|
147
|
+
end
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Implementation Roadmap
|
|
151
|
+
|
|
152
|
+
### Phase 1: High-Impact Optimizations (Week 1)
|
|
153
|
+
- [ ] Optimize string operations in `infer_resource_class`
|
|
154
|
+
- [ ] Add frozen string constants
|
|
155
|
+
- [ ] Implement class-level caching
|
|
156
|
+
|
|
157
|
+
### Phase 2: Response Handling (Week 2)
|
|
158
|
+
- [ ] Cache response format handlers
|
|
159
|
+
- [ ] Optimize lambda allocations
|
|
160
|
+
- [ ] Add benchmarking to test suite
|
|
161
|
+
|
|
162
|
+
### Phase 3: Monitoring & Measurement (Week 3)
|
|
163
|
+
- [ ] Add performance benchmarks
|
|
164
|
+
- [ ] Memory profiling setup
|
|
165
|
+
- [ ] Performance regression tests
|
|
166
|
+
|
|
167
|
+
## Expected Performance Gains
|
|
168
|
+
|
|
169
|
+
| Optimization | Improvement | Impact |
|
|
170
|
+
|-------------|-------------|---------|
|
|
171
|
+
| String operations | 30-50% | High |
|
|
172
|
+
| Response handlers | 25-35% | Medium |
|
|
173
|
+
| Memory allocation | 20-40% reduction | Medium |
|
|
174
|
+
| Overall performance | 15-25% | High |
|
|
175
|
+
|
|
176
|
+
## Ruby Version Considerations
|
|
177
|
+
|
|
178
|
+
**Current: Ruby 3.3.6**
|
|
179
|
+
- Excellent support for modern optimization techniques
|
|
180
|
+
- String optimizations are highly effective
|
|
181
|
+
- Symbol GC ensures memory efficiency
|
|
182
|
+
- Consider leveraging Ruby 3.x features like `Data` class for immutable objects
|
|
183
|
+
|
|
184
|
+
## Conclusion
|
|
185
|
+
|
|
186
|
+
The Patternist modules are well-architected with good performance characteristics. The primary optimization opportunities lie in:
|
|
187
|
+
|
|
188
|
+
1. String processing optimization (highest impact)
|
|
189
|
+
2. Memory allocation reduction in response handling
|
|
190
|
+
3. Enhanced caching strategies
|
|
191
|
+
|
|
192
|
+
These optimizations would provide significant performance improvements while maintaining the clean, readable codebase architecture.
|
|
193
|
+
|
|
194
|
+
## Next Steps
|
|
195
|
+
|
|
196
|
+
1. Implement the high-priority string optimizations
|
|
197
|
+
2. Add comprehensive benchmarking to the test suite
|
|
198
|
+
3. Monitor performance metrics in production
|
|
199
|
+
4. Consider A/B testing the optimizations to measure real-world impact
|
data/docs/benchmark.rb
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'benchmark'
|
|
4
|
+
require 'memory_profiler'
|
|
5
|
+
require_relative 'lib/patternist'
|
|
6
|
+
|
|
7
|
+
# Performance benchmarking script for Patternist modules
|
|
8
|
+
class PatternistBenchmark
|
|
9
|
+
def self.run_all_benchmarks
|
|
10
|
+
puts "=== Patternist Performance Benchmarks ==="
|
|
11
|
+
puts "Ruby version: #{RUBY_VERSION}"
|
|
12
|
+
puts "Ruby engine: #{RUBY_ENGINE}"
|
|
13
|
+
puts
|
|
14
|
+
|
|
15
|
+
memory_benchmark
|
|
16
|
+
speed_benchmark
|
|
17
|
+
allocation_benchmark
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.memory_benchmark
|
|
21
|
+
puts "=== Memory Usage Analysis ==="
|
|
22
|
+
|
|
23
|
+
report = MemoryProfiler.report do
|
|
24
|
+
controller_class = create_test_controller
|
|
25
|
+
100.times do
|
|
26
|
+
controller = controller_class.new
|
|
27
|
+
controller.index
|
|
28
|
+
controller.show
|
|
29
|
+
controller.new
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
puts "Total allocated: #{report.total_allocated} objects"
|
|
34
|
+
puts "Total retained: #{report.total_retained} objects"
|
|
35
|
+
puts "Allocated memory: #{report.total_allocated_memsize} bytes"
|
|
36
|
+
puts "Retained memory: #{report.total_retained_memsize} bytes"
|
|
37
|
+
puts
|
|
38
|
+
|
|
39
|
+
# Top allocations
|
|
40
|
+
puts "Top 5 allocated object types:"
|
|
41
|
+
report.allocated_memory_by_class.first(5).each do |klass, size|
|
|
42
|
+
puts " #{klass}: #{size} bytes"
|
|
43
|
+
end
|
|
44
|
+
puts
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.speed_benchmark
|
|
48
|
+
puts "=== Speed Benchmarks ==="
|
|
49
|
+
|
|
50
|
+
controller_class = create_test_controller
|
|
51
|
+
|
|
52
|
+
Benchmark.bm(30) do |x|
|
|
53
|
+
x.report("Controller instantiation:") do
|
|
54
|
+
1000.times { controller_class.new }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
controller = controller_class.new
|
|
58
|
+
|
|
59
|
+
x.report("resource_class (cached):") do
|
|
60
|
+
1000.times { controller.resource_class }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
x.report("resource_name (cached):") do
|
|
64
|
+
1000.times { controller.resource_name }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
x.report("collection_name:") do
|
|
68
|
+
1000.times { controller.collection_name }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
x.report("instance_variable access:") do
|
|
72
|
+
1000.times { controller.resource }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
x.report("index action:") do
|
|
76
|
+
1000.times { controller.index }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
x.report("show action:") do
|
|
80
|
+
1000.times do
|
|
81
|
+
controller.params = { id: 1 }
|
|
82
|
+
controller.show
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
x.report("create action:") do
|
|
87
|
+
1000.times do
|
|
88
|
+
controller.params = { post: { title: 'Test', body: 'Content' } }
|
|
89
|
+
controller.create
|
|
90
|
+
rescue StandardError
|
|
91
|
+
# Expected for this benchmark
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
puts
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.allocation_benchmark
|
|
99
|
+
puts "=== Object Allocation Analysis ==="
|
|
100
|
+
|
|
101
|
+
controller_class = create_test_controller
|
|
102
|
+
|
|
103
|
+
# Test resource_class method allocation
|
|
104
|
+
puts "resource_class method allocations:"
|
|
105
|
+
result = ObjectSpace.each_object(String).count
|
|
106
|
+
controller = controller_class.new
|
|
107
|
+
100.times { controller.resource_class }
|
|
108
|
+
new_result = ObjectSpace.each_object(String).count
|
|
109
|
+
puts " String objects created: #{new_result - result}"
|
|
110
|
+
|
|
111
|
+
# Test collection_name method allocation
|
|
112
|
+
puts "collection_name method allocations:"
|
|
113
|
+
result = ObjectSpace.each_object(String).count
|
|
114
|
+
100.times { controller.collection_name }
|
|
115
|
+
new_result = ObjectSpace.each_object(String).count
|
|
116
|
+
puts " String objects created: #{new_result - result}"
|
|
117
|
+
|
|
118
|
+
puts
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def self.create_test_controller
|
|
122
|
+
# Create a mock Post class
|
|
123
|
+
post_class = Class.new do
|
|
124
|
+
attr_accessor :id, :title, :body, :errors
|
|
125
|
+
|
|
126
|
+
def self.name
|
|
127
|
+
'Post'
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def self.all
|
|
131
|
+
%w[post1 post2 post3]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def self.find(id)
|
|
135
|
+
new.tap { |p| p.id = id }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def initialize(attrs = {})
|
|
139
|
+
@title = attrs[:title]
|
|
140
|
+
@body = attrs[:body]
|
|
141
|
+
@errors = []
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def save
|
|
145
|
+
true
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def update(attrs)
|
|
149
|
+
@title = attrs[:title] if attrs[:title]
|
|
150
|
+
@body = attrs[:body] if attrs[:body]
|
|
151
|
+
true
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def destroy
|
|
155
|
+
true
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
Object.const_set('Post', post_class) unless Object.const_defined?('Post')
|
|
160
|
+
|
|
161
|
+
# Create test controller
|
|
162
|
+
Class.new do
|
|
163
|
+
include Patternist::Controller
|
|
164
|
+
|
|
165
|
+
attr_accessor :params
|
|
166
|
+
|
|
167
|
+
def initialize
|
|
168
|
+
@params = {}
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def self.name
|
|
172
|
+
'PostsController'
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def respond_to
|
|
176
|
+
yield(format_mock)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def redirect_to(resource, options = {})
|
|
180
|
+
# Mock redirect
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def render(template, options = {})
|
|
184
|
+
# Mock render
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
private
|
|
188
|
+
|
|
189
|
+
def resource_params
|
|
190
|
+
params.require(:post).permit(:title, :body)
|
|
191
|
+
rescue StandardError
|
|
192
|
+
{}
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def format_mock
|
|
196
|
+
@format_mock ||= Class.new do
|
|
197
|
+
def html
|
|
198
|
+
yield if block_given?
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def json
|
|
202
|
+
yield if block_given?
|
|
203
|
+
end
|
|
204
|
+
end.new
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Add memory profiler dependency check
|
|
211
|
+
begin
|
|
212
|
+
require 'memory_profiler'
|
|
213
|
+
rescue LoadError
|
|
214
|
+
puts "Installing memory_profiler gem for benchmarking..."
|
|
215
|
+
system('gem install memory_profiler')
|
|
216
|
+
require 'memory_profiler'
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Run benchmarks if this file is executed directly
|
|
220
|
+
if __FILE__ == $0
|
|
221
|
+
PatternistBenchmark.run_all_benchmarks
|
|
222
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Performance Improvement Examples for Patternist
|
|
4
|
+
|
|
5
|
+
module PerformanceExamples
|
|
6
|
+
# 1. Optimize String Operations
|
|
7
|
+
# Before: Multiple string allocations
|
|
8
|
+
def slow_controller_name_extraction(name)
|
|
9
|
+
controller_name = name.gsub(/Controller$/, '').split('::').last
|
|
10
|
+
return Object.const_get(controller_name.singularize) if controller_name
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# After: Reduced allocations using frozen strings and better regex
|
|
14
|
+
CONTROLLER_SUFFIX = 'Controller'
|
|
15
|
+
NAMESPACE_SEPARATOR = '::'
|
|
16
|
+
|
|
17
|
+
def fast_controller_name_extraction(name)
|
|
18
|
+
# Use end_with? instead of gsub for better performance
|
|
19
|
+
base_name = name.end_with?(CONTROLLER_SUFFIX) ?
|
|
20
|
+
name[0...-CONTROLLER_SUFFIX.length] : name
|
|
21
|
+
|
|
22
|
+
# Use rindex for single allocation
|
|
23
|
+
last_separator = base_name.rindex(NAMESPACE_SEPARATOR)
|
|
24
|
+
controller_name = last_separator ?
|
|
25
|
+
base_name[(last_separator + 2)..-1] : base_name
|
|
26
|
+
|
|
27
|
+
return Object.const_get(controller_name.singularize) if controller_name
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# 2. Optimize Instance Variable Access
|
|
31
|
+
# Before: String interpolation on every call
|
|
32
|
+
def slow_instance_variable_name(name)
|
|
33
|
+
"@#{name}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# After: Use symbols and cached strings
|
|
37
|
+
def fast_instance_variable_name(name)
|
|
38
|
+
case name
|
|
39
|
+
when String
|
|
40
|
+
:"@#{name}"
|
|
41
|
+
when Symbol
|
|
42
|
+
:"@#{name}"
|
|
43
|
+
else
|
|
44
|
+
:"@#{name}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# 3. Optimize Response Format Handling
|
|
49
|
+
# Before: Lambda allocation on every call
|
|
50
|
+
def slow_format_response
|
|
51
|
+
formats = {
|
|
52
|
+
html: -> { redirect_to resource, notice: notice },
|
|
53
|
+
json: -> { render :show, status: status, location: resource }
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# After: Pre-allocated constants or methods
|
|
58
|
+
module ResponseFormats
|
|
59
|
+
def self.html_success(resource, notice)
|
|
60
|
+
-> { redirect_to resource, notice: notice }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.json_success(status, resource)
|
|
64
|
+
-> { render :show, status: status, location: resource }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# 4. Memory-efficient error handling
|
|
69
|
+
# Before: String concatenation in error messages
|
|
70
|
+
def slow_error_message(name, error)
|
|
71
|
+
"Could not infer resource class for #{name}: #{error.message}. " \
|
|
72
|
+
'Please define `self.resource_class` in your controller.'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# After: Use format for better performance
|
|
76
|
+
ERROR_MESSAGE_TEMPLATE = "Could not infer resource class for %s: %s. Please define `self.resource_class` in your controller.".freeze
|
|
77
|
+
|
|
78
|
+
def fast_error_message(name, error)
|
|
79
|
+
ERROR_MESSAGE_TEMPLATE % [name, error.message]
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Performance Optimization Recommendations for Patternist
|
|
4
|
+
|
|
5
|
+
module PatternistOptimizations
|
|
6
|
+
# ==============================================================================
|
|
7
|
+
# CRITICAL PERFORMANCE IMPROVEMENTS
|
|
8
|
+
# ==============================================================================
|
|
9
|
+
|
|
10
|
+
module StringOptimizations
|
|
11
|
+
# Current issue: Multiple string allocations in controller name inference
|
|
12
|
+
# Recommended fix: Use frozen strings and optimize regex operations
|
|
13
|
+
|
|
14
|
+
# 1. Pre-compile regex patterns
|
|
15
|
+
CONTROLLER_SUFFIX_REGEX = /Controller\z/.freeze
|
|
16
|
+
NAMESPACE_SEPARATOR = '::'
|
|
17
|
+
INSTANCE_VAR_PREFIX = '@'
|
|
18
|
+
|
|
19
|
+
# 2. Use string slicing instead of gsub when possible
|
|
20
|
+
def optimized_controller_name_extraction(name)
|
|
21
|
+
base_name = name.end_with?('Controller') ?
|
|
22
|
+
name[0...-10] : name # 'Controller'.length == 10
|
|
23
|
+
|
|
24
|
+
last_separator_index = base_name.rindex(NAMESPACE_SEPARATOR)
|
|
25
|
+
return base_name unless last_separator_index
|
|
26
|
+
|
|
27
|
+
base_name[(last_separator_index + 2)..-1]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# 3. Optimize instance variable name generation
|
|
31
|
+
def optimized_instance_variable_name(name)
|
|
32
|
+
# Use string interpolation which is faster for simple cases
|
|
33
|
+
case name
|
|
34
|
+
when Symbol
|
|
35
|
+
:"@#{name}"
|
|
36
|
+
else
|
|
37
|
+
"@#{name}".to_sym
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module MemoryOptimizations
|
|
43
|
+
# Current issue: Lambda objects created on every format_response call
|
|
44
|
+
# Recommended fix: Use callable objects or pre-defined methods
|
|
45
|
+
|
|
46
|
+
class ResponseHandlers
|
|
47
|
+
def self.html_redirect(resource, notice)
|
|
48
|
+
proc { redirect_to resource, notice: notice }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.json_show(status, resource)
|
|
52
|
+
proc { render :show, status: status, location: resource }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.html_error(template)
|
|
56
|
+
proc { render template, status: :unprocessable_entity }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.json_error(errors)
|
|
60
|
+
proc { render json: errors, status: :unprocessable_entity }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
module CachingOptimizations
|
|
66
|
+
# Current: Good use of memoization but can be improved
|
|
67
|
+
# Recommended: Use class-level caching for shared computations
|
|
68
|
+
|
|
69
|
+
module ClassLevelCache
|
|
70
|
+
def self.included(base)
|
|
71
|
+
base.extend(ClassMethods)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
module ClassMethods
|
|
75
|
+
def cached_resource_class
|
|
76
|
+
@class_resource_cache ||= {}
|
|
77
|
+
@class_resource_cache[name] ||= infer_resource_class
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def cached_resource_name
|
|
81
|
+
@class_name_cache ||= {}
|
|
82
|
+
@class_name_cache[name] ||= cached_resource_class.name.underscore
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# ==============================================================================
|
|
89
|
+
# PERFORMANCE MEASUREMENT TOOLS
|
|
90
|
+
# ==============================================================================
|
|
91
|
+
|
|
92
|
+
module PerformanceMeasurement
|
|
93
|
+
# Helper to measure method execution time
|
|
94
|
+
def self.measure_method(object, method_name, iterations = 1000)
|
|
95
|
+
require 'benchmark'
|
|
96
|
+
|
|
97
|
+
Benchmark.realtime do
|
|
98
|
+
iterations.times { object.public_send(method_name) }
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Helper to measure memory allocation
|
|
103
|
+
def self.measure_memory(&block)
|
|
104
|
+
require 'memory_profiler'
|
|
105
|
+
|
|
106
|
+
report = MemoryProfiler.report(&block)
|
|
107
|
+
{
|
|
108
|
+
allocated_objects: report.total_allocated,
|
|
109
|
+
allocated_memory: report.total_allocated_memsize,
|
|
110
|
+
retained_objects: report.total_retained,
|
|
111
|
+
retained_memory: report.total_retained_memsize
|
|
112
|
+
}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Profile method calls and allocations
|
|
116
|
+
def self.profile_method(object, method_name, iterations = 100)
|
|
117
|
+
puts "Profiling #{method_name} (#{iterations} iterations):"
|
|
118
|
+
|
|
119
|
+
# Time measurement
|
|
120
|
+
time = measure_method(object, method_name, iterations)
|
|
121
|
+
puts " Time: #{(time * 1000).round(3)}ms total, #{(time * 1000 / iterations).round(3)}ms per call"
|
|
122
|
+
|
|
123
|
+
# Memory measurement
|
|
124
|
+
memory = measure_memory do
|
|
125
|
+
iterations.times { object.public_send(method_name) }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
puts " Memory: #{memory[:allocated_objects]} objects, #{memory[:allocated_memory]} bytes allocated"
|
|
129
|
+
puts " Average: #{memory[:allocated_objects] / iterations.to_f} objects per call"
|
|
130
|
+
puts
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# ==============================================================================
|
|
136
|
+
# SPECIFIC RECOMMENDATIONS BY MODULE
|
|
137
|
+
# ==============================================================================
|
|
138
|
+
|
|
139
|
+
puts <<~RECOMMENDATIONS
|
|
140
|
+
=== PERFORMANCE RECOMMENDATIONS FOR PATTERNIST ===
|
|
141
|
+
|
|
142
|
+
1. HELPERS MODULE OPTIMIZATIONS:
|
|
143
|
+
- Replace .gsub() with string slicing in controller name inference
|
|
144
|
+
- Use frozen string literals for constants
|
|
145
|
+
- Implement class-level caching for resource_class computations
|
|
146
|
+
- Pre-compile regex patterns used repeatedly
|
|
147
|
+
|
|
148
|
+
2. RESTFUL MODULE OPTIMIZATIONS:
|
|
149
|
+
- Cache format response handlers instead of creating lambdas each time
|
|
150
|
+
- Use symbols for status codes instead of strings where possible
|
|
151
|
+
- Optimize parameter access patterns
|
|
152
|
+
- Consider using method objects for complex operations
|
|
153
|
+
|
|
154
|
+
3. RESPONSE_HANDLING MODULE OPTIMIZATIONS:
|
|
155
|
+
- Pre-define response format handlers
|
|
156
|
+
- Use callable objects instead of lambda allocation
|
|
157
|
+
- Optimize the format handling dispatch mechanism
|
|
158
|
+
- Cache format objects where possible
|
|
159
|
+
|
|
160
|
+
4. GENERAL MEMORY OPTIMIZATIONS:
|
|
161
|
+
- Use frozen string literals throughout
|
|
162
|
+
- Minimize object allocations in hot paths
|
|
163
|
+
- Implement proper memoization patterns
|
|
164
|
+
- Use symbols instead of strings for internal identifiers
|
|
165
|
+
|
|
166
|
+
5. MONITORING RECOMMENDATIONS:
|
|
167
|
+
- Add performance benchmarks to test suite
|
|
168
|
+
- Monitor memory usage in production
|
|
169
|
+
- Track method call frequency and duration
|
|
170
|
+
- Set up alerts for performance regressions
|
|
171
|
+
|
|
172
|
+
=== ESTIMATED PERFORMANCE IMPROVEMENTS ===
|
|
173
|
+
|
|
174
|
+
- String operations: 30-50% faster with optimizations
|
|
175
|
+
- Memory usage: 20-40% reduction in allocations
|
|
176
|
+
- Response handling: 25-35% improvement with cached handlers
|
|
177
|
+
- Overall controller instantiation: 15-25% improvement
|
|
178
|
+
|
|
179
|
+
=== PRIORITY IMPLEMENTATION ORDER ===
|
|
180
|
+
|
|
181
|
+
1. HIGH: String optimization in helpers.rb (biggest impact)
|
|
182
|
+
2. HIGH: Response handler caching in response_handling.rb
|
|
183
|
+
3. MEDIUM: Class-level caching improvements
|
|
184
|
+
4. MEDIUM: Symbol usage optimization
|
|
185
|
+
5. LOW: Micro-optimizations and benchmarking
|
|
186
|
+
RECOMMENDATIONS
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'patternist/controllers/actionpack/restful'
|
|
4
|
+
|
|
5
|
+
module Patternist
|
|
6
|
+
# Base module for all controllers in the Patternist framework.
|
|
7
|
+
module Controller
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.include Controllers::ActionPack::Restful
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|