provision 0.0.1
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.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +157 -0
- data/Rakefile +4 -0
- data/lib/provision/version.rb +3 -0
- data/lib/provision.rb +94 -0
- data/provision.gemspec +21 -0
- data/spec/provision/sample.rb +3 -0
- data/spec/provision_spec.rb +99 -0
- data/spec/spec_helper.rb +1 -0
- metadata +78 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Chen Fisher
|
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,157 @@
|
|
1
|
+
# Provision
|
2
|
+
|
3
|
+
The provision gem lets you hook a provision filter to a method. **ANY method**.
|
4
|
+
|
5
|
+
For example,
|
6
|
+
Lets say we have an article website, where users can post articles.
|
7
|
+
|
8
|
+
We want to limit the number of articles a user can post to our website, based on a primium account. For example, if the user is using the free version he can post up to 5 articles, when a user that has paid and is using the pro account, can post up to 20 articles.
|
9
|
+
|
10
|
+
This is where 'provision' comes in. It makes it easy and dry to do provisions on your website.
|
11
|
+
|
12
|
+
The great thing about **provision** is that it can act on any level - model, controller or any other class you have.
|
13
|
+
Think of it as ActiveRecord's **validate** and Controller's **before_filter** combined and available on any class and method.
|
14
|
+
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
gem 'provision'
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
$ bundle
|
25
|
+
|
26
|
+
Or install it yourself as:
|
27
|
+
|
28
|
+
$ gem install provision
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
When a method is provisioned, whenever it is called, the provision method comes into action.
|
32
|
+
If the provision method returns true then the original method will be called. If the provision returns false
|
33
|
+
then the method won't be called (and false will be returned)
|
34
|
+
|
35
|
+
### Provision using a method
|
36
|
+
class SomeClass
|
37
|
+
def a_method
|
38
|
+
# do something
|
39
|
+
end
|
40
|
+
provision :a_method, :with => :provision_method
|
41
|
+
|
42
|
+
private
|
43
|
+
def provision_method
|
44
|
+
# some condition (returns true or false)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
**provision_method** can take optional ***args** which are the arguments passed to the original method:
|
49
|
+
|
50
|
+
def provision_method(*args)
|
51
|
+
# some condition using args
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
### Provision using a proc (lambda)
|
56
|
+
class SomeClass
|
57
|
+
def a_method
|
58
|
+
# do something
|
59
|
+
end
|
60
|
+
provision :a_method, :with => lambda{ [some condition] }
|
61
|
+
end
|
62
|
+
|
63
|
+
**lambda** can take arguments like so:
|
64
|
+
|
65
|
+
provision :a_method, :with => lambda{ |sender, *args| [some condition] }
|
66
|
+
where **sender** is the instance of SomeClass and ***args** are the arguments passed to the original method
|
67
|
+
|
68
|
+
|
69
|
+
### Provision using a class name
|
70
|
+
class SomeClass
|
71
|
+
def a_method
|
72
|
+
# do something
|
73
|
+
end
|
74
|
+
provision :a_method, :with => ProvisionClass
|
75
|
+
end
|
76
|
+
|
77
|
+
**NOTE**: provision will try to call `ProvisionClass.provision!` (a class method); if the class does not respond to provision! then
|
78
|
+
it will create an instance of ProvisionClass and then call `provision!` (mind the bang!)
|
79
|
+
|
80
|
+
class ProvisionClass
|
81
|
+
def self.provision!(sender, *args)
|
82
|
+
# some condition using sender and *args
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
**sender** is the instance of SomeClass and ***args** are the arguments passed to the original method
|
87
|
+
|
88
|
+
### Provision using an instance of a class
|
89
|
+
when the **lambda** block return something other than *true* or *false* then provision assumes it returns an instance
|
90
|
+
of a class that responds to **provision!**
|
91
|
+
|
92
|
+
provision :a_method, :with => lambda{ |sender, *args| ProvisionClass.new(sender, *args) }
|
93
|
+
|
94
|
+
**sender** and ***args** are optional
|
95
|
+
|
96
|
+
### Add a block to do something
|
97
|
+
You can add a block which will be called when the provision returns false.
|
98
|
+
|
99
|
+
provision :a_method, :with => lambda{ some_condition } do |sender, *args|
|
100
|
+
# do something for when the provision fails
|
101
|
+
# for example: send a notification email to the user you just provisioned
|
102
|
+
end
|
103
|
+
|
104
|
+
**sender** and ***args** are optional
|
105
|
+
|
106
|
+
### Provision more than one method at a time
|
107
|
+
You can pass any number of methods to be provisioned
|
108
|
+
|
109
|
+
provision :a_method, :another_method, :with => lambda{ some_condition } do
|
110
|
+
# do something
|
111
|
+
end
|
112
|
+
|
113
|
+
### Use it in your controllers and models
|
114
|
+
You can use it in your controllers and models like you use before_filter and validations.
|
115
|
+
Lets provision a user on the number of posts they can post. We can do this on the controller level or on the model level:
|
116
|
+
|
117
|
+
#### Controller level example:
|
118
|
+
class PostsController < ApplicationController
|
119
|
+
include provision
|
120
|
+
|
121
|
+
def create
|
122
|
+
# create the post
|
123
|
+
end
|
124
|
+
|
125
|
+
provision :create, :with => UserProvision do |controller|
|
126
|
+
controller.flash[:error] = "You have maxed out your posts"
|
127
|
+
redirect_to some_url
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class UserProvision
|
132
|
+
def self.provision!(controller)
|
133
|
+
controller.current_user.post_count < MAX_POSTS
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
#### Model level example:
|
138
|
+
class Post < ActiveRecord::Base
|
139
|
+
include provision
|
140
|
+
|
141
|
+
provision :save, :valid?, :with => lambda{ |post| post.user.post_count < MAX_COUNT } do |post|
|
142
|
+
post.errors[:count] = "you have maxed out you post count"
|
143
|
+
false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
## GOTCHA!
|
148
|
+
`provision` call **must** come **after** your provisioned method definitions
|
149
|
+
|
150
|
+
|
151
|
+
## Contributing
|
152
|
+
|
153
|
+
1. Fork it
|
154
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
155
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
156
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
157
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/provision.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require "provision/version"
|
2
|
+
|
3
|
+
module Provision
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
##
|
7
|
+
#
|
8
|
+
def provision(*args, &block)
|
9
|
+
options = args.last.is_a?(::Hash) ? args.pop : {}
|
10
|
+
|
11
|
+
hook(*args, :before => options[:with], &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# hooks the specified array of methods to a method specified with the :before hook
|
16
|
+
# calls the original method only if the hooked method returns true
|
17
|
+
# usage:
|
18
|
+
# hook :method1, :method2, :before => :method_name
|
19
|
+
def hook(*args, &block)
|
20
|
+
hooks = args.last.is_a?(::Hash) ? args.pop : {}
|
21
|
+
|
22
|
+
args.each do |method|
|
23
|
+
original_method = self.instance_method(method)
|
24
|
+
|
25
|
+
self.send(:define_method, method) do |*a|
|
26
|
+
return original_method.bind(self).call(*a) if
|
27
|
+
case
|
28
|
+
when hooks[:before].is_a?(Symbol)
|
29
|
+
hook_method(hooks[:before], *a)
|
30
|
+
when hooks[:before].is_a?(Class)
|
31
|
+
hook_class(hooks[:before], *a)
|
32
|
+
when hooks[:before].is_a?(Proc)
|
33
|
+
hook_proc(hooks[:before], *a)
|
34
|
+
when hooks[:before].instance_of?(hooks[:before].class)
|
35
|
+
hook_instance(hooks[:before], *a)
|
36
|
+
end
|
37
|
+
|
38
|
+
if block_given?
|
39
|
+
if block.arity == 0
|
40
|
+
return yield
|
41
|
+
else
|
42
|
+
return yield(self, *a)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private :hook
|
52
|
+
end
|
53
|
+
|
54
|
+
module InstanceMethods
|
55
|
+
def hook_method(h, *a)
|
56
|
+
self.send(h, *a)
|
57
|
+
end
|
58
|
+
|
59
|
+
def hook_class(h, *a)
|
60
|
+
if h.respond_to? :provision!
|
61
|
+
h.provision!(self, *a)
|
62
|
+
else
|
63
|
+
h.new.provision!(self, *a)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def hook_instance(h, *a)
|
68
|
+
h.provision!(self, *a)
|
69
|
+
end
|
70
|
+
|
71
|
+
def hook_proc(h, *a)
|
72
|
+
# call the proc
|
73
|
+
if h.arity == 0
|
74
|
+
result = h.call
|
75
|
+
else
|
76
|
+
result = h.call(self, *a)
|
77
|
+
end
|
78
|
+
|
79
|
+
# if proc returns true or false then return the result
|
80
|
+
return result if result == true || result == false
|
81
|
+
|
82
|
+
# if proc returns an instance of an object then call provision! on it
|
83
|
+
result.provision!(*a)
|
84
|
+
end
|
85
|
+
|
86
|
+
private :hook_method, :hook_class, :hook_instance, :hook_proc
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.included(receiver)
|
91
|
+
receiver.extend ClassMethods
|
92
|
+
receiver.send :include, InstanceMethods
|
93
|
+
end
|
94
|
+
end
|
data/provision.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'provision/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "provision"
|
8
|
+
gem.version = Provision::VERSION
|
9
|
+
gem.authors = ["Chen Fisher"]
|
10
|
+
gem.email = ["chen.fisher@gmail.com"]
|
11
|
+
gem.description = %q{Lets one hook a provision for a method}
|
12
|
+
gem.summary = %q{When a method is provisioned, any call to that method is passed through a provision filter that can stop the method execution and perform some actions}
|
13
|
+
gem.homepage = "https://github.com/chenfisher/provision"
|
14
|
+
|
15
|
+
gem.add_development_dependency "rspec"
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($/)
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.require_paths = ["lib"]
|
21
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'provision/sample'
|
3
|
+
|
4
|
+
describe Provision do
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@sample = Sample.new
|
8
|
+
@sample.stub(:a_method).and_return('hello')
|
9
|
+
@meta = class << @sample; self; end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should call the method if provision returns true" do
|
13
|
+
@sample.stub(:this_method).and_return(true)
|
14
|
+
@meta.provision :a_method, :with => :this_method
|
15
|
+
|
16
|
+
@sample.a_method.should eq 'hello'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should provision a method using a method name" do
|
20
|
+
@sample.stub(:this_method).and_return(false)
|
21
|
+
@meta.provision :a_method, :with => :this_method
|
22
|
+
|
23
|
+
@sample.a_method.should_not eq 'hello'
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should provision a method using a class name" do
|
27
|
+
class ThisClass;
|
28
|
+
def provision!(sender)
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
@meta.provision :a_method, :with => ThisClass
|
34
|
+
|
35
|
+
@sample.a_method.should_not eq 'hello'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should provision a method using a class name with class method provision!" do
|
39
|
+
class ThisClass;
|
40
|
+
def self.provision!(sender)
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
@meta.provision :a_method, :with => ThisClass
|
46
|
+
|
47
|
+
@sample.a_method.should_not eq 'hello'
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should provision a method using an instance of a class" do
|
51
|
+
class ThisClass; end
|
52
|
+
@instance = ThisClass.new
|
53
|
+
@instance.stub(:provision!).and_return false
|
54
|
+
@instance.should_receive(:provision!)
|
55
|
+
|
56
|
+
@meta.provision :a_method, :with => @instance
|
57
|
+
|
58
|
+
@sample.a_method.should_not eq 'hello'
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should provision a method using an instance of a class (from a proc)" do
|
62
|
+
class ThisClass; end
|
63
|
+
ThisClass.any_instance.stub(:provision!).and_return false
|
64
|
+
ThisClass.any_instance.should_receive(:provision!)
|
65
|
+
|
66
|
+
@meta.provision :a_method, :with => lambda{ ThisClass.new }
|
67
|
+
|
68
|
+
@sample.a_method.should_not eq 'hello'
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should provision a method using a proc" do
|
72
|
+
@meta.provision :a_method, :with => lambda{ false }
|
73
|
+
|
74
|
+
@sample.a_method.should_not eq 'hello'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should takea proc with arguments" do
|
78
|
+
@meta.provision :a_method, :with => lambda{ |sender, p| sender === @sample && p == 'p' }
|
79
|
+
|
80
|
+
@sample.a_method('p').should eq 'hello'
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should take an array of methods to provision" do
|
84
|
+
@sample.stub(:b_method).and_return('hello')
|
85
|
+
@meta.provision :a_method, :b_method, :with => lambda{ false }
|
86
|
+
|
87
|
+
@sample.a_method.should_not eq 'hello'
|
88
|
+
@sample.b_method.should_not eq 'hello'
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should take a block as an action when provision fails" do
|
92
|
+
@meta.provision :a_method, :with => lambda{ false } do
|
93
|
+
'hi there'
|
94
|
+
end
|
95
|
+
|
96
|
+
@sample.a_method.should eq 'hi there'
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'provision'
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: provision
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Chen Fisher
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: Lets one hook a provision for a method
|
31
|
+
email:
|
32
|
+
- chen.fisher@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- .rspec
|
39
|
+
- CHANGELOG.md
|
40
|
+
- Gemfile
|
41
|
+
- LICENSE.txt
|
42
|
+
- README.md
|
43
|
+
- Rakefile
|
44
|
+
- lib/provision.rb
|
45
|
+
- lib/provision/version.rb
|
46
|
+
- provision.gemspec
|
47
|
+
- spec/provision/sample.rb
|
48
|
+
- spec/provision_spec.rb
|
49
|
+
- spec/spec_helper.rb
|
50
|
+
homepage: https://github.com/chenfisher/provision
|
51
|
+
licenses: []
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
requirements: []
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.8.24
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: When a method is provisioned, any call to that method is passed through a
|
74
|
+
provision filter that can stop the method execution and perform some actions
|
75
|
+
test_files:
|
76
|
+
- spec/provision/sample.rb
|
77
|
+
- spec/provision_spec.rb
|
78
|
+
- spec/spec_helper.rb
|