restomatic 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: 5e40b314565dc501d5da5f6b97f108c41c4700a940208b056af5205fa681cb4d
4
+ data.tar.gz: c93b2fc63f639ef8a899e29934b20c7a0492869acdd195a9758c17c4a143ecb2
5
+ SHA512:
6
+ metadata.gz: 7e895db99eb254ea860f56e46d371b327576d0403c72520f1f2a7578b7f245b23a1268e79c9ff4a7476e8bdd5e00d1331d38b4df5b634176245c4161c4165e61
7
+ data.tar.gz: 560e927399392480f81eeffaeb45653b4f9a0d4aed1838f5f914bbc2389442f19f23b1b1aea919758adc27b77195ad2437ff2db6b93e50879ec0b0f11f387912
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2022 Brad Gessler
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # RESTomatic
2
+
3
+ RESTomatic helps Rails developers organize nested resources with automatic namespacing. Each nested resource gets its own controller module, making your app cleaner and easier to maintain.
4
+
5
+ Unlike Rails shallow routes that send everything to one controller, RESTomatic enforces proper separation: `Blogs::PostsController`, `Users::PostsController`, etc. keeping your controllers organized and your codebase more maintainable.
6
+
7
+ ## Installation
8
+
9
+ Add to your Rails application Gemfile:
10
+
11
+ ```ruby
12
+ bundle add "restomatic"
13
+ ```
14
+ That's it! The routing helpers are automatically available in your `config/routes.rb` file.
15
+
16
+ Then start using more RESTful routes in `config/routes.rb`:
17
+
18
+ ```ruby
19
+ resources :blogs do
20
+ nest :posts do # Blogs::PostsController
21
+ collection do
22
+ get :search # Blogs::PostsController#search
23
+ end
24
+ end
25
+ end
26
+
27
+ resources :posts do
28
+ create :comments # Posts::CommentsController#new, #create
29
+ end
30
+ ```
31
+
32
+ ## The Problem
33
+
34
+ Rails provides shallow routes and nested resources, but the syntax becomes verbose and repetitive, especially when you want to properly namespace controllers.
35
+
36
+ ### Before RESTomatic
37
+
38
+ ```ruby
39
+ resources :blogs do
40
+ scope module: :blogs do
41
+ resources :posts, only: %i[index new create] do
42
+ collection do
43
+ get :search
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ resources :posts do
50
+ scope module: :posts do
51
+ resources :comments, only: %i[new create]
52
+ end
53
+ end
54
+ ```
55
+
56
+ ### With RESTomatic
57
+
58
+ ```ruby
59
+ resources :blogs do
60
+ nest :posts do # Blogs::PostsController
61
+ collection do
62
+ get :search # Blogs::PostsController#search
63
+ end
64
+ end
65
+ end
66
+
67
+ resources :posts do
68
+ create :comments # Posts::CommentsController#new, #create
69
+ end
70
+ ```
71
+
72
+ Much cleaner! The `nest` helper automatically:
73
+ - Scopes to the parent resource's module (e.g., `Blogs::PostsController`)
74
+ - Sets sensible defaults for which actions are included
75
+ - Reduces boilerplate while maintaining Rails conventions
76
+
77
+ ## Route Helpers
78
+
79
+ ### `nest`
80
+
81
+ Nest resources under a parent with automatic module scoping and sensible action defaults.
82
+
83
+ ```ruby
84
+ resources :blogs do
85
+ nest :posts # Creates index, new, create actions under Blogs::PostsController
86
+ nest :post # Singular: creates new, create actions (no edit/update/destroy)
87
+ end
88
+ ```
89
+
90
+ **Options:**
91
+ - `except:` - Exclude specific actions (overrides defaults)
92
+ - Singular resources default to excluding: `[:edit, :update, :destroy]`
93
+ - Plural resources default to excluding: `[:show, :edit, :update, :destroy]`
94
+
95
+ **Module-only nesting:**
96
+
97
+ ```ruby
98
+ resources :posts do
99
+ nest do
100
+ # Routes defined here will be scoped to Posts module
101
+ # without creating a nested resource
102
+ get :analytics
103
+ end
104
+ end
105
+ ```
106
+
107
+ ### `create`
108
+
109
+ Define routes for creating a resource (new + create actions only).
110
+
111
+ ```ruby
112
+ resources :posts do
113
+ create :comments # Only new and create actions
114
+ end
115
+
116
+ create :session # Works with both singular and plural forms
117
+ ```
118
+
119
+ ### `edit`
120
+
121
+ Define routes for editing a resource (edit + update actions only).
122
+
123
+ ```ruby
124
+ resources :posts do
125
+ edit :metadata # Only edit and update actions
126
+ end
127
+ ```
128
+
129
+ ### `show`
130
+
131
+ Define routes for showing a resource (show action only).
132
+
133
+ ```ruby
134
+ resources :posts do
135
+ show :preview # Only show action
136
+ end
137
+ ```
138
+
139
+ ### `destroy`
140
+
141
+ Define routes for destroying a resource (destroy action only).
142
+
143
+ ```ruby
144
+ resources :posts do
145
+ destroy :attachment # Only destroy action
146
+ end
147
+ ```
148
+
149
+ ### `list`
150
+
151
+ Define routes for listing resources (index action only).
152
+
153
+ ```ruby
154
+ resources :users do
155
+ list :posts # Only index action
156
+ end
157
+ ```
158
+
159
+ ## Real-World Example
160
+
161
+ ```ruby
162
+ Rails.application.routes.draw do
163
+ root "home#index"
164
+
165
+ # Public blog routes
166
+ resources :blogs, only: [:index, :show] do
167
+ list :posts # Blogs::PostsController#index
168
+ end
169
+
170
+ # Admin area with nested resources
171
+ namespace :admin do
172
+ resources :blogs do
173
+ nest :posts do # Admin::Blogs::PostsController
174
+ create :comments # Admin::Blogs::Posts::CommentsController#new, #create
175
+ collection do
176
+ get :scheduled # Admin::Blogs::PostsController#scheduled
177
+ post :bulk_publish # Admin::Blogs::PostsController#bulk_publish
178
+ end
179
+ end
180
+ end
181
+
182
+ resources :posts do
183
+ edit :seo # Admin::Posts::SeosController#edit, #update
184
+ show :preview # Admin::Posts::PreviewsController#show
185
+ destroy :featured_image # Admin::Posts::FeaturedImagesController#destroy
186
+ end
187
+ end
188
+
189
+ # User account management
190
+ resource :account do
191
+ nest do
192
+ edit :profile # Accounts::ProfilesController#edit, #update
193
+ edit :password # Accounts::PasswordsController#edit, #update
194
+ show :billing # Accounts::BillingsController#show
195
+ end
196
+ end
197
+ end
198
+ ```
199
+
200
+ ## How It Works
201
+
202
+ RESTomatic extends `ActionDispatch::Routing::Mapper` to add these helper methods. The helpers automatically:
203
+
204
+ 1. Detect singular vs. plural resource names
205
+ 2. Apply appropriate module scoping
206
+ 3. Set sensible action defaults based on the helper used
207
+ 4. Work seamlessly with existing Rails routing features
208
+
209
+ ## Philosophy
210
+
211
+ Rails routing is powerful but can become verbose when building properly organized applications with shallow routes and namespaced controllers. RESTomatic embraces Rails conventions while reducing boilerplate, making your routes file more readable and maintainable.
212
+
213
+ ## Requirements
214
+
215
+ - Rails 7.0+
216
+ - Ruby 3.0+
217
+
218
+ ## Contributing
219
+
220
+ 1. Fork the repository
221
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
222
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
223
+ 4. Push to the branch (`git push origin my-new-feature`)
224
+ 5. Create a new Pull Request
225
+
226
+ ## License
227
+
228
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/setup"
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,97 @@
1
+ module ActionDispatch::Routing
2
+ # I have no idea how or why this works this way, I lifted the pattern from Devise, which came with even
3
+ # more weird stuff. Rails could use an API for adding route helpers to decrease the brittleness of this
4
+ # approach. For now, deal with this helper.
5
+ class Mapper
6
+ def nest(name = nil, *args, except: nil, **kwargs, &block)
7
+ unless resource_scope?
8
+ raise ArgumentError, "can't use nest outside resource(s) scope"
9
+ end
10
+
11
+ if name.nil?
12
+ scope module: parent_module_name, &block
13
+ elsif is_singular_resource_name? name
14
+ scope module: parent_module_name do
15
+ except ||= %i[edit update destroy]
16
+ resource name, *args, except: except, **kwargs, &block
17
+ end
18
+ else
19
+ scope module: parent_module_name do
20
+ except ||= %i[show edit update destroy]
21
+ resources name, *args, except: except, **kwargs, &block
22
+ end
23
+ end
24
+ end
25
+
26
+ def create(name, *args, **kwargs, &block)
27
+ if resource_scope? && !@scope[:module]
28
+ scope module: parent_module_name do
29
+ resource name, *args, only: %i[new create], **kwargs, &block
30
+ end
31
+ else
32
+ resource name, *args, only: %i[new create], **kwargs, &block
33
+ end
34
+ end
35
+
36
+ def edit(name, *args, **kwargs, &block)
37
+ if resource_scope? && !@scope[:module]
38
+ scope module: parent_module_name do
39
+ resource name, *args, only: %i[edit update], **kwargs, &block
40
+ end
41
+ else
42
+ resource name, *args, only: %i[edit update], **kwargs, &block
43
+ end
44
+ end
45
+
46
+ def show(name, *args, **kwargs, &block)
47
+ if resource_scope? && !@scope[:module]
48
+ scope module: parent_module_name do
49
+ resource name, *args, only: :show, **kwargs, &block
50
+ end
51
+ else
52
+ resource name, *args, only: :show, **kwargs, &block
53
+ end
54
+ end
55
+
56
+ def destroy(name, *args, **kwargs, &block)
57
+ if resource_scope? && !@scope[:module]
58
+ scope module: parent_module_name do
59
+ resource name, *args, only: :destroy, **kwargs, &block
60
+ end
61
+ else
62
+ resource name, *args, only: :destroy, **kwargs, &block
63
+ end
64
+ end
65
+
66
+ def index(name, *args, **kwargs, &block)
67
+ if resource_scope? && !@scope[:module]
68
+ scope module: parent_module_name do
69
+ resources name, *args, only: :index, **kwargs, &block
70
+ end
71
+ else
72
+ resources name, *args, only: :index, **kwargs, &block
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def parent_module_name
79
+ # For singular resources (resource :account), Rails uses plural controller names (AccountsController)
80
+ # So we need to pluralize the parent resource name for the module scope
81
+ parent_resource.singular ? parent_resource.name.to_s.pluralize : parent_resource.name
82
+ end
83
+
84
+ def is_singular_resource_name?(name)
85
+ name_string = name.to_s
86
+ name_string.singularize == name_string
87
+ end
88
+
89
+ def resource_plurality(name)
90
+ is_singular_resource_name?(name) ? :resource : :resources
91
+ end
92
+
93
+ def inflect_resource_plurality(name, *args, **kwargs, &)
94
+ self.method(resource_plurality(name)).call(name, *args, **kwargs, &)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,3 @@
1
+ module Restomatic
2
+ VERSION = "0.1.0"
3
+ end
data/lib/restomatic.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "restomatic/version"
2
+ require "restomatic/routing_mapper"
3
+
4
+ module Restomatic
5
+ # Rails routing mapper extensions for cleaner route definitions
6
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: restomatic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Brad Gessler
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-11-18 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
26
+ description: Provides cleaner, more intuitive helpers for defining RESTful routes
27
+ with proper scoping and namespacing in Rails
28
+ email:
29
+ - bradgessler@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - MIT-LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - lib/restomatic.rb
38
+ - lib/restomatic/routing_mapper.rb
39
+ - lib/restomatic/version.rb
40
+ homepage: https://github.com/rocketshipio/restomatic
41
+ licenses:
42
+ - MIT
43
+ metadata:
44
+ allowed_push_host: https://rubygems.org
45
+ homepage_uri: https://github.com/rocketshipio/restomatic
46
+ source_code_uri: https://github.com/rocketshipio/restomatic
47
+ changelog_uri: https://github.com/rocketshipio/restomatic
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '3.0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubygems_version: 3.6.2
63
+ specification_version: 4
64
+ summary: Better route mappers for Rails applications
65
+ test_files: []