customs 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []