customs 1.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
+ SHA1:
3
+ metadata.gz: 4f7d1115e1d61873619f4bab8f5eee93db18d59d
4
+ data.tar.gz: e3a3b2c4954b7f009fcbfdcab5b45ac505a4e7b8
5
+ SHA512:
6
+ metadata.gz: 6cb37e479f2d906090756750d72355485b0cc54d396b2e2b5d19e25a6f3a56fe2f0d53c170449fabebb3402923def6ddb8d048e7331dbe8ddb6bdfb65ece2890
7
+ data.tar.gz: 10d4143ff33e16680a0db8a3f4c42f8c744f04f7e3952553d50a56cd26cb660e7d0a93ba32e4174af23b1531975f23f5c34e0cfd1b63bef202a77cc28daa6198
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in customs.gemspec
4
+ gemspec
5
+
6
+ case ENV['RAILS_VERSION'] || 'default'
7
+ when 'default'
8
+ gem 'rails'
9
+ when 'master'
10
+ gem 'rails', github: 'rails/rails'
11
+ when %r{\A3\.}
12
+ gem 'rails', "~> #{ENV['RAILS_VERSION']}"
13
+ else
14
+ gem 'rails', "~> #{ENV['RAILS_VERSION']}"
15
+ end
16
+
17
+ gem 'rspec'
18
+ gem 'rspec-rails'
19
+ gem 'awesome_print'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Savater Sebastien
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,229 @@
1
+ <img src="http://upload.wikimedia.org/wikipedia/commons/3/36/Aiga_customs_inv.gif" alt="Customs Icon" align="right" width="120" />
2
+
3
+ # Customs
4
+
5
+ <img src="https://travis-ci.org/plataformatec/devise.svg?branch=master">
6
+
7
+
8
+ **Customs** uses the power of [cancancan](https://github.com/CanCanCommunity/cancancan) (formerly known as [cancan](http://github.com/ryanb/cancan/)) to control the flow of your controllers.
9
+
10
+ It adds some magic in your rails controllers, through the cancan magic formula : ```load_and_authorize_resource```, and let you customize the flow.
11
+
12
+ **Customs** provides you:
13
+
14
+ * default cruds methods
15
+ * full control on controllers methods, steps by steps
16
+ * methods for HTTP statuses
17
+ * common errors rescue
18
+
19
+ ```
20
+ class DrogsController < ApplicationController
21
+ control_and_rescue_traffic
22
+ respond_to :html, :json
23
+
24
+ load_and_authorize_resource :drog
25
+ end
26
+ ```
27
+
28
+ In the given examples, we do not endorse in any way the traffic of illegal products.
29
+ Customs is watching you!
30
+
31
+
32
+ ## Requirements:
33
+
34
+ * Ruby 1.9.3
35
+ * Rails 3.2.18 and higher
36
+ * Cancancan
37
+
38
+
39
+ ## Installation
40
+
41
+ Add this line to your application's Gemfile:
42
+
43
+ gem 'customs'
44
+
45
+ Or install it yourself as:
46
+
47
+ $ gem install customs
48
+
49
+ Then, take a beer.
50
+
51
+ ## Usage
52
+
53
+ Two methods available:
54
+
55
+ * ``control_traffic``
56
+ * ``rescue_traffic``
57
+
58
+ Or only one to bind them:
59
+
60
+ * ``control_and_rescue_traffic``
61
+
62
+
63
+ #### Example
64
+
65
+ ```
66
+ class DrogsController < ApplicationController
67
+ load_and_authorize_resource :drog
68
+ control_and_rescue_traffic
69
+ respond_to :html, :json
70
+ end
71
+ ```
72
+
73
+
74
+ #### Control traffic
75
+
76
+ `control_traffic` provides you default methods to list, create, update & delete resources, depending on the cancan `load_resource` arguments.
77
+
78
+ It uses exception to control the flow of data, such as:
79
+
80
+ * ActiveRecord::RecordNotFound or Mongoid::Errors::DocumentNotFound
81
+ * ActiveRecord::RecordInvalid or Mongoid::Errors::Validations
82
+ * CanCan::AccessDenied
83
+
84
+
85
+ #### Rescue traffic
86
+
87
+ `rescue_traffic` provides methods for specific HTTP statuses & routes the flow exceptions to the most appropriate one.
88
+
89
+ * 401 - `unauthorized`
90
+ * 403 - `forbidden`
91
+ * 404 - `not_found`
92
+ * 422 - `unprocessable`
93
+
94
+
95
+ #### Callbacks
96
+
97
+ Callbacks are working like rails filters :
98
+
99
+ * `before_save :make_something`
100
+ * `before_save :make_something_else, only: :create`
101
+ * `before_destroy :make_nothing`
102
+
103
+ ```
104
+ class BaggagesController < ApplicationController
105
+ load_and_authorize_resource :baggage
106
+ before_save :add_illegal_content
107
+ after_save :report_to_autorithies
108
+ after_destroy :analyse_scraps
109
+ end
110
+ ```
111
+
112
+ Maybe you prefer to pass a block to `create`, `update` or `destroy` ?
113
+ In this case, the block is called after the `save!` method.
114
+
115
+ ```
116
+ class BaggagesController < ApplicationController
117
+ load_and_authorize_resource :baggage
118
+
119
+ def create
120
+ super do
121
+ control_content && report_to_autorithies
122
+ end
123
+ end
124
+ end
125
+ ```
126
+
127
+
128
+ #### Flow customization
129
+
130
+ You can customize the controller flow by overriding any following methods:
131
+
132
+ * `resource_params` - parameters attributes, which will be used to create/update resources
133
+ * `success_response` - what happened after successfull action
134
+ * `resource_location` - where redirect on a success response (depending on the `action_name`)
135
+
136
+ For a better understanding, you have to keep in mind that `control_traffic` apply these methods with the following schema:
137
+
138
+
139
+ ```
140
+ class DrogsController < ApplicationController
141
+
142
+ def create
143
+ resource.assign_attributes resource_params
144
+ resource.save!
145
+ success_response
146
+ end
147
+
148
+ def update
149
+ resource.assign_attributes resource_params
150
+ resource.save!
151
+ success_response
152
+ end
153
+
154
+ def destroy
155
+ resource.destroy
156
+ success_response
157
+ end
158
+
159
+ protected
160
+
161
+ def resource_params
162
+ params[resource_name]
163
+ end
164
+
165
+ def success_response
166
+ respond_with resource, :location => resource_location
167
+ end
168
+
169
+ def resource_location
170
+ case action_name
171
+ when 'create' then resource
172
+ when 'update' then resource
173
+ when 'destroy' then resource_name.to_s.pluralize.to_sym
174
+ end
175
+ end
176
+ end
177
+ ```
178
+
179
+
180
+ ##### Mass assignment
181
+
182
+ I highly recommend overriding the `resource_params` method with [Strong Parameters](http://github.com/rails/strong_parameters):
183
+
184
+ ```
185
+ class SmugglersController < ApplicationController
186
+ load_and_authorize_resource :smuggler
187
+
188
+ protected
189
+
190
+ def resource_params
191
+ params.require(:smuggler).permit(:name, :skills)
192
+ end
193
+ end
194
+ ```
195
+
196
+
197
+ #### Trace errors
198
+
199
+ With `rescue_traffic`, rescued errors will output something like that in you logs:
200
+
201
+ ```
202
+ [Customs] Rescuing from ActionView::MissingTemplate with :not_found
203
+ ```
204
+
205
+ If you need to trace the error, you just have to override the `rescue_exception_with` method:
206
+
207
+ ```
208
+ def rescue_exception_with exception, method
209
+ logger.error "Rescuing from #{ exception.class } with :#{ method }"
210
+ exception.backtrace[0..10].each {|line| logger.error line }
211
+ send method
212
+ end
213
+
214
+ ```
215
+
216
+ ## Contributing
217
+
218
+ 1. Fork it
219
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
220
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
221
+ 4. Push to the branch (`git push origin my-new-feature`)
222
+ 5. Create new Pull Request
223
+
224
+
225
+ #### Credits
226
+
227
+ - The gem has been cutted over [cancan](https://github.com/ryanb/cancan).
228
+ - The icon has been stolen to [wikimedia.org](http://wikimedia.org).
229
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new('spec')
6
+
7
+ task default: :spec
@@ -0,0 +1,3 @@
1
+ .dialog
2
+ %h1= I18n.t 'customs.401.title'
3
+ %p= I18n.t 'customs.401.content'
@@ -0,0 +1,3 @@
1
+ .dialog
2
+ %h1= I18n.t 'customs.403.title'
3
+ %p= I18n.t 'customs.403.content'
@@ -0,0 +1,3 @@
1
+ .dialog
2
+ %h1= I18n.t 'customs.404.title'
3
+ %p= I18n.t 'customs.404.content'
@@ -0,0 +1,3 @@
1
+ .dialog
2
+ %h1= I18n.t 'customs.406.title'
3
+ %p= I18n.t 'customs.406.content'
@@ -0,0 +1,14 @@
1
+ en:
2
+ customs:
3
+ "401":
4
+ title: You cannot access to this page.
5
+ content: You first need to authenticate to access this page.
6
+ "403":
7
+ title: You cannot access to this page.
8
+ content: You do not have sufficient permissions to access this page.
9
+ "404":
10
+ title: The page you were looking for doesn't exist.
11
+ content: You may have mistyped the address or the page may have moved.
12
+ "406":
13
+ title: The resource you have requested cannot be returned.
14
+ content: An appropriate representation of the requested resource could not be rendered.
@@ -0,0 +1,106 @@
1
+ require 'cancan'
2
+
3
+ module Customs
4
+ module ControlFlow
5
+
6
+ # GET actions
7
+
8
+ def index
9
+ respond_with resource_collection
10
+ end
11
+
12
+ [:new, :show, :edit].each do |name|
13
+ define_method name do
14
+ respond_with resource
15
+ end
16
+ end
17
+
18
+ # Other actions
19
+
20
+ def create
21
+ save_resource
22
+ success_response
23
+ end
24
+
25
+ def update
26
+ save_resource
27
+ success_response
28
+ end
29
+
30
+ def destroy
31
+ destroy_resource
32
+ success_response
33
+ end
34
+
35
+
36
+ protected
37
+
38
+ # Resource naming
39
+
40
+ def resource_name
41
+ @resource_name ||= resource_name_from_controller
42
+ end
43
+
44
+ def resource_name_from_controller
45
+ params[:controller].sub("Controller", "").underscore.split('/').last.singularize
46
+ end
47
+
48
+ def resource_class
49
+ @resource_class ||= resource_name.to_s.classify.constantize
50
+ end
51
+
52
+ def resource_params
53
+ params[resource_name]
54
+ end
55
+
56
+ # Resources
57
+
58
+ def resource_collection
59
+ instance_variable_get "@#{resource_name.to_s.pluralize}"
60
+ end
61
+
62
+ def resource
63
+ instance_variable_get "@#{resource_name.to_s}"
64
+ end
65
+
66
+ # Resource actions
67
+
68
+ def save_resource
69
+ resource.assign_attributes resource_params
70
+
71
+ args = [:save]
72
+ args << action_name unless ::Rails.version.to_f >= 4.0
73
+
74
+ run_callbacks *args do
75
+ resource.save!
76
+ end
77
+ end
78
+
79
+ def destroy_resource
80
+ args = [:destroy]
81
+ args << action_name unless ::Rails.version.to_f >= 4.0
82
+
83
+ run_callbacks *args do
84
+ if resource.respond_to? :destroy!
85
+ resource.destroy!
86
+ else
87
+ resource.destroy
88
+ end
89
+ end
90
+ end
91
+
92
+ # Response
93
+
94
+ def success_response
95
+ respond_with resource, :location => resource_location
96
+ end
97
+
98
+ def resource_location
99
+ case action_name
100
+ when 'create' then resource
101
+ when 'update' then resource
102
+ when 'destroy' then resource_name.to_s.pluralize.to_sym
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,60 @@
1
+ require 'cancan'
2
+
3
+ module Customs
4
+
5
+ # Override CanCan::ControllerResource
6
+
7
+ class ControllerResource < CanCan::ControllerResource
8
+ def initialize *args
9
+ super
10
+ @controller.resource_name = instance_name
11
+ @controller.resource_class = resource_class
12
+ end
13
+
14
+ def build_resource
15
+ resource = resource_base.new
16
+ assign_attributes(resource)
17
+ end
18
+ end
19
+
20
+ module ControlTraffic
21
+
22
+ def self.included base
23
+ base.send :extend, ClassMethods
24
+
25
+ base.class_attribute :controller_resource_class, :instance_reader => false
26
+ base.controller_resource_class = ControllerResource
27
+
28
+ base.class_attribute :resource_name, :instance_reader => false
29
+ base.class_attribute :resource_class, :instance_reader => false
30
+
31
+ base.define_callbacks :save, :destroy
32
+ end
33
+
34
+ module ClassMethods
35
+ def cancan_resource_class
36
+ if ancestors.map(&:to_s).include? "InheritedResources::Actions"
37
+ super
38
+ else
39
+ self.controller_resource_class
40
+ end
41
+ end
42
+
43
+
44
+ [:before, :after, :around].each do |callback|
45
+
46
+ define_method "#{callback}_save" do |*names, &blk|
47
+ _insert_callbacks(names, blk) do |name, options|
48
+ set_callback :save, callback, name, options
49
+ end
50
+ end
51
+
52
+ define_method "#{callback}_destroy" do |*names, &blk|
53
+ _insert_callbacks(names, blk) do |name, options|
54
+ set_callback :destroy, callback, name, options
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,28 @@
1
+ require 'rails/engine'
2
+
3
+ module Customs
4
+ class Engine < Rails::Engine
5
+
6
+ initializer 'traffic.configure' do
7
+ ActiveSupport.on_load(:action_controller) do
8
+ ActionController::Base.send :extend, Customs::ControllerAdditions
9
+ end
10
+ end
11
+ end
12
+
13
+ module ControllerAdditions
14
+ def control_and_rescue_traffic
15
+ control_traffic && rescue_traffic
16
+ end
17
+
18
+ def control_traffic
19
+ include Customs::ControlTraffic
20
+ include Customs::ControlFlow
21
+ end
22
+
23
+ def rescue_traffic
24
+ include Customs::Statuses
25
+ include Customs::RescueTraffic
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ module Customs
2
+ module RescueTraffic
3
+
4
+ def self.included base
5
+ base.rescue_from CanCan::AccessDenied, with: proc{|e| rescue_exception_with e, :access_denied }
6
+
7
+ base.rescue_from ActionController::UnknownFormat, with: proc{|e| rescue_exception_with e, :not_acceptable } if defined? ActionController::UnknownFormat
8
+
9
+ base.rescue_from ActiveRecord::RecordNotFound, with: proc{|e| rescue_exception_with e, :not_found } if defined? ActiveRecord::RecordNotFound
10
+ base.rescue_from ActiveRecord::RecordInvalid , with: proc{|e| rescue_exception_with e, :unprocessable } if defined? ActiveRecord::RecordInvalid
11
+
12
+ base.rescue_from Mongoid::Errors::DocumentNotFound, with: proc{|e| rescue_exception_with e, :not_found } if defined? Mongoid::Errors
13
+ base.rescue_from Mongoid::Errors::Validations , with: proc{|e| rescue_exception_with e, :unprocessable } if defined? Mongoid::Errors
14
+ end
15
+
16
+ def rescue_exception_with exception, status_method
17
+ if respond_to?(:logger) && logger
18
+ logger.info "[Customs] Rescuing from #{ exception.class } with :#{ status_method }"
19
+ end
20
+
21
+ send status_method
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,53 @@
1
+ module Customs
2
+ module Statuses
3
+
4
+ def self.included base
5
+ base.class_attribute :navigational_formats
6
+ base.navigational_formats = [:html]
7
+ end
8
+
9
+ { :unauthorized => 401,
10
+ :forbidden => 403,
11
+ :not_found => 404,
12
+ :not_acceptable => 406,
13
+ :unprocessable => 422
14
+ }.each do |method, code|
15
+ define_method method do
16
+ render options_for_status_code(code)
17
+ false
18
+ end
19
+ end
20
+
21
+ def access_denied
22
+ return unauthorized if self.respond_to?(:current_user) && !current_user
23
+ forbidden
24
+ end
25
+
26
+ def unprocessable *args
27
+ options = args.extract_options!
28
+
29
+ respond_to do |format|
30
+ format.any *navigational_formats do
31
+ action = options.delete(:action)
32
+ action ||= case params[:action]
33
+ when 'update' then 'edit'
34
+ when 'create' then 'new'
35
+ else raise "Unknown unprocessable action"
36
+ end
37
+
38
+ render :status => 422, :action => action
39
+ end
40
+
41
+ format.all { render :status => 422 }
42
+ end
43
+ end
44
+
45
+ def options_for_status_code code
46
+ { :status => code, :template => template_for_status_code(code) }
47
+ end
48
+
49
+ def template_for_status_code code
50
+ "customs/#{code}"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Customs
2
+ VERSION = '1.1.0'
3
+ end
data/lib/customs.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'customs/statuses'
2
+ require 'customs/rescue_traffic'
3
+ require 'customs/control_traffic'
4
+ require 'customs/control_flow'
5
+ require 'customs/engine'
6
+ require 'customs/version'
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: customs
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Savater
8
+ - Sebastien
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 3.2.0
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 3.2.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: sqlite3
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: railties
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: 3.2.0
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 3.2.0
56
+ - !ruby/object:Gem::Dependency
57
+ name: activesupport
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.2.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 3.2.0
70
+ - !ruby/object:Gem::Dependency
71
+ name: cancancan
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ description: Use the power of Cancan to control the flow of your controllers.
85
+ email: savater.sebastien@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - Gemfile
91
+ - LICENSE
92
+ - README.md
93
+ - Rakefile
94
+ - app/views/customs/401.html.haml
95
+ - app/views/customs/403.html.haml
96
+ - app/views/customs/404.html.haml
97
+ - app/views/customs/406.html.haml
98
+ - config/locales/en.yml
99
+ - lib/customs.rb
100
+ - lib/customs/control_flow.rb
101
+ - lib/customs/control_traffic.rb
102
+ - lib/customs/engine.rb
103
+ - lib/customs/rescue_traffic.rb
104
+ - lib/customs/statuses.rb
105
+ - lib/customs/version.rb
106
+ homepage: http://github.com/inkstak/customs
107
+ licenses: []
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 1.9.0
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.2.2
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Control the traffic of your rails controllers.
129
+ test_files: []