crudable-rails 1.0.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: c3bce80de66d640b96222a505af0357771fe9657d14ae853649bbdf7c78778bc
4
+ data.tar.gz: 435d8c280e42996182578d23065d2905ca39aa6608e324b410eac295933a33aa
5
+ SHA512:
6
+ metadata.gz: be090adbbbf6dce74144fa5c5ee435fb17f85dc674505ab4244101db8bbdb35cb634a8da2e662f76e83dca14d0b411effbcad0dafa263ca98c1c044e0f1e84f6
7
+ data.tar.gz: 1175cca36ec335d808dbe2f7740efd2e9c6043b58b7cf45a7fbe753f0e315b7873a44fbf9ee56256766157468a4b92855a48c44ce8f1c7001bf66959efe3b104
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2023 Tomislav Simnett
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,28 @@
1
+ # Crudable
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem "crudable"
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install crudable
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ 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,3 @@
1
+ require "bundler/setup"
2
+
3
+ require "bundler/gem_tasks"
@@ -0,0 +1,6 @@
1
+ en:
2
+ crudable:
3
+ created: "%{model_name} successfully created"
4
+ updated: "%{model_name} successfully updated"
5
+ destroyed: "%{model_name} successfully destroyed"
6
+ not_destroyed: "%{model_name} could not be destroyed"
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crudable
4
+ module Rails
5
+ module Base
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include Crudable::Rails::Resourceable
10
+
11
+ before_action :find_resource, only: %i[show edit update destroy]
12
+ after_action :authorize_resource, only: %i[new show edit] if defined?(Pundit)
13
+
14
+ decorates_assigned plural_resource_var_name.to_sym, resource_var_name.to_sym if defined?(Draper)
15
+ end
16
+
17
+ def index
18
+ instance_variable_set("@#{plural_resource_var_name}", resource_scope)
19
+ paginate_resource
20
+ end
21
+
22
+ def show; end
23
+
24
+ def new
25
+ instance_variable_set("@#{resource_var_name}", resource_class.new)
26
+ end
27
+
28
+ def create # rubocop:disable Metrics/MethodLength
29
+ instance_variable_set("@#{resource_var_name}", resource_class.new(create_params)) unless skip_initialize_create?
30
+ before_authorize_create
31
+ authorize_resource
32
+ after_authorize_create
33
+ if instance_variable_get("@#{resource_var_name}").save
34
+ on_successful_create
35
+ on_successful_create_render
36
+ else
37
+ on_failed_create_setup
38
+ on_failed_create_render
39
+ end
40
+ end
41
+
42
+ def edit; end
43
+
44
+ def update
45
+ before_authorize_update
46
+ authorize_resource
47
+ after_authorize_update
48
+ if instance_variable_get("@#{resource_var_name}").update update_params
49
+ on_successful_update
50
+ on_successful_update_render
51
+ else
52
+ on_failed_update_setup
53
+ on_failed_update_render
54
+ end
55
+ end
56
+
57
+ def destroy
58
+ authorize_resource(destroy_method)
59
+ if instance_variable_get("@#{resource_var_name}").send(destroy_method)
60
+ on_successful_destroy_render
61
+ else
62
+ on_failed_destroy_render
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def destroy_method
69
+ return :destroy unless defined?(Discard)
70
+ return :destroy if params[:destroy]
71
+
72
+ @destroy_method ||= discard? && !instance_variable_get("@#{resource_var_name}").discarded? ? :discard : :destroy
73
+ end
74
+
75
+ def discard?
76
+ false
77
+ end
78
+
79
+ def find_resource
80
+ resource = friendly_finders? ? resource_class.friendly : resource_class
81
+ instance = singleton? ? resource.first : resource.find(params[finder_param])
82
+
83
+ return redirect_to (resource_namespace + [instance]), status: :moved_permanently if should_redirect?(instance)
84
+
85
+ instance_variable_set("@#{resource_var_name}", instance)
86
+ end
87
+
88
+ def should_redirect?(instance)
89
+ friendly_finders? && request.path != polymorphic_path(resource_namespace + [instance])
90
+ end
91
+
92
+ def singleton?
93
+ false
94
+ end
95
+
96
+ def finder_param
97
+ :id
98
+ end
99
+
100
+ def resource_scope
101
+ return apply_scopes(authorizable_scope) if defined?(HasScope)
102
+
103
+ authorizable_scope
104
+ end
105
+
106
+ def authorizable_scope
107
+ defined?(Pundit) ? policy_scope(resource_class) : resource_class.all
108
+ end
109
+
110
+ def paginate_resource?
111
+ return false unless defined?(Kaminari)
112
+
113
+ true
114
+ end
115
+
116
+ def friendly_finders?
117
+ return false unless defined?(FriendlyId) && object_class.respond_to?(:friendly)
118
+
119
+ true
120
+ end
121
+
122
+ def skip_initialize_create?
123
+ false
124
+ end
125
+
126
+ def paginate_resource
127
+ return unless paginate_resource?
128
+
129
+ instance_variable_set(
130
+ "@#{plural_resource_var_name}",
131
+ instance_variable_get("@#{plural_resource_var_name}").page(params[:page]).per(params[:per])
132
+ )
133
+ end
134
+
135
+ def after_create_redirect_path
136
+ resource_namespace + [plural_resource_var_name.to_sym]
137
+ end
138
+
139
+ def after_create_notice
140
+ t('crudable.created', model_name: resource_class.model_name.human)
141
+ end
142
+
143
+ def after_update_redirect_path
144
+ resource_namespace + [instance_variable_get("@#{resource_var_name}")]
145
+ end
146
+
147
+ def after_update_notice
148
+ t('crudable.updated', model_name: resource_class.model_name.human)
149
+ end
150
+
151
+ def after_destroy_redirect_path
152
+ resource_namespace + [plural_resource_var_name.to_sym]
153
+ end
154
+
155
+ def after_failed_destroy_redirect_path
156
+ after_destroy_redirect_path
157
+ end
158
+
159
+ def after_destroy_notice
160
+ t('crudable.destroyed', model_name: resource_class.model_name.human)
161
+ end
162
+
163
+ def after_failed_destroy_alert
164
+ t('crudable.not_destroyed', model_name: resource_class.model_name.human)
165
+ end
166
+
167
+ def on_successful_create; end
168
+
169
+ def on_successful_create_render
170
+ redirect_to after_create_redirect_path, success: after_create_notice
171
+ end
172
+
173
+ def on_failed_create_setup; end
174
+
175
+ def on_successful_update; end
176
+
177
+ def on_successful_update_render
178
+ redirect_to after_update_redirect_path, success: after_update_notice
179
+ end
180
+
181
+ def on_failed_update_setup; end
182
+
183
+ def on_successful_destroy_render
184
+ redirect_to after_destroy_redirect_path, success: after_destroy_notice
185
+ end
186
+
187
+ def on_failed_destroy_render
188
+ redirect_to after_failed_destroy_redirect_path, alert: after_failed_destroy_alert
189
+ end
190
+
191
+ def before_authorize_create; end
192
+
193
+ def before_authorize_update; end
194
+
195
+ def after_authorize_create; end
196
+
197
+ def after_authorize_update; end
198
+
199
+ def on_failed_create_render
200
+ respond_to do |format|
201
+ format.turbo_stream
202
+ format.html { render :new, status: :unprocessable_entity }
203
+ end
204
+ end
205
+
206
+ def on_failed_update_render
207
+ respond_to do |format|
208
+ format.turbo_stream
209
+ format.html { render :edit, status: :unprocessable_entity }
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crudable
4
+ module Rails
5
+ # Include Crudable Controller Class Methods
6
+ module Controller
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def crudable(nested: false)
11
+ include Crudable::Rails::Base
12
+ include Crudable::Rails::Nestable if nested
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Crudable
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ ActiveSupport.on_load(:action_controller) do
5
+ ::ActionController::Base.include Crudable::Rails::Controller
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crudable
4
+ module Rails
5
+ # Nestable Module
6
+ module Nestable
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ before_action :find_parent, only: %i[index new create]
11
+ end
12
+
13
+ def find_parent
14
+ params.each do |name, value|
15
+ next unless name =~ /(.+)_id$/
16
+
17
+ object_name = Regexp.last_match(1)
18
+ object_scope = object_name.classify.constantize
19
+ object_scope = object_class.friendly if friendly_finders?
20
+ instance_variable_set("@#{object_name}", object_scope.find(value))
21
+ self.class.send(:decorates_assigned, object_name.to_sym) if defined?(Draper)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crudable
4
+ module Rails
5
+ # Resourceable
6
+ #
7
+ # Provides a number of resource helpers for controllers supporting Crudable
8
+ module Resourceable
9
+ extend ActiveSupport::Concern
10
+
11
+ def resource_name
12
+ self.class.name.split('::').last.gsub('Controller', '').singularize
13
+ end
14
+
15
+ def resource_human_name
16
+ resource_name.humanize
17
+ end
18
+
19
+ def resource_class
20
+ resource_name.constantize
21
+ end
22
+
23
+ def resource_var_name
24
+ resource_name.underscore
25
+ end
26
+
27
+ def plural_resource_var_name
28
+ resource_name.pluralize.underscore
29
+ end
30
+
31
+ def resource_namespace
32
+ namespaces = self.class.name.split('::')
33
+ namespaces.pop
34
+ namespaces.map { |n| n.downcase.to_sym }
35
+ end
36
+
37
+ def authorize_resource(method = action_name)
38
+ authorize authorizable_resource, "#{method}?".to_sym
39
+ end
40
+
41
+ def authorizable_resource
42
+ instance_variable_get("@#{resource_var_name}")
43
+ end
44
+
45
+ class_methods do
46
+ def resource_name
47
+ name.split('::').last.gsub('Controller', '').singularize
48
+ end
49
+
50
+ def resource_class
51
+ resource_name.constantize
52
+ end
53
+
54
+ def resource_var_name
55
+ resource_name.underscore
56
+ end
57
+
58
+ def plural_resource_var_name
59
+ resource_name.pluralize.underscore
60
+ end
61
+
62
+ def resource_namespace
63
+ namespaces = name.split('::')
64
+ namespaces.pop
65
+ namespaces.map { |n| n.downcase.to_sym }
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Crudable Version
4
+ module Crudable
5
+ module Rails
6
+ VERSION = '1.0.0'
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ require 'crudable/rails/version'
2
+ require 'crudable/rails/engine'
3
+ require 'crudable/rails/controller'
4
+ require 'crudable/rails/base'
5
+ require 'crudable/rails/resourceable'
6
+ require 'crudable/rails/nestable'
7
+
8
+ module Crudable
9
+ module Rails
10
+ # Your code goes here...
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :crudable do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crudable-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Tomislav Simnett
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-10-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.50'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.50'
41
+ description: CRUD operations for Rails controllers
42
+ email:
43
+ - tom@initforthe.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - MIT-LICENSE
49
+ - README.md
50
+ - Rakefile
51
+ - config/locales/en.yml
52
+ - lib/crudable-rails.rb
53
+ - lib/crudable/rails/base.rb
54
+ - lib/crudable/rails/controller.rb
55
+ - lib/crudable/rails/engine.rb
56
+ - lib/crudable/rails/nestable.rb
57
+ - lib/crudable/rails/resourceable.rb
58
+ - lib/crudable/rails/version.rb
59
+ - lib/tasks/crudable_tasks.rake
60
+ homepage: https://gitlab.com/initforthe/crudable-rails
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 3.3.26
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: CRUD operations for Rails controllers
83
+ test_files: []