rulebook 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -1
- data/Gemfile +7 -0
- data/Gemfile.lock +21 -0
- data/LICENSE +1 -1
- data/README.md +41 -47
- data/Rakefile +33 -54
- data/VERSION +1 -1
- data/examples/simple.rb +43 -0
- data/lib/rulebook.rb +14 -153
- data/lib/rulebook/class_methods.rb +6 -0
- data/lib/rulebook/core_ext/module.rb +24 -0
- data/lib/rulebook/instance_methods.rb +41 -0
- data/lib/rulebook/rule.rb +18 -0
- data/rulebook.gemspec +49 -29
- data/test/helper.rb +10 -8
- data/test/test_chevy.rb +56 -69
- data/test/test_rule.rb +10 -0
- metadata +147 -54
- data/.gitignore +0 -21
- data/TODO +0 -1
- data/test/test_class_methods.rb +0 -53
- data/test/test_rules.rb +0 -90
- data/test/test_ryguy.rb +0 -45
- data/test/test_user.rb +0 -48
data/.document
CHANGED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
GEM
|
2
|
+
specs:
|
3
|
+
git (1.2.5)
|
4
|
+
jeweler (1.5.2)
|
5
|
+
bundler (~> 1.0.0)
|
6
|
+
git (>= 1.2.5)
|
7
|
+
rake
|
8
|
+
meta_tools (0.1.0)
|
9
|
+
rake (0.8.7)
|
10
|
+
riot (0.12.3)
|
11
|
+
rr
|
12
|
+
rr (1.0.2)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
x86-mingw32
|
16
|
+
|
17
|
+
DEPENDENCIES
|
18
|
+
jeweler
|
19
|
+
meta_tools
|
20
|
+
rake
|
21
|
+
riot
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# rulebook
|
1
|
+
# rulebook ![](http://stillmaintained.com/c00lryguy/rulebook.png)
|
2
2
|
|
3
3
|
Allows you to define a set of 'rules' or dynamic methods to apply to a class.
|
4
4
|
|
@@ -7,68 +7,56 @@ Allows you to define a set of 'rules' or dynamic methods to apply to a class.
|
|
7
7
|
> gem update --system
|
8
8
|
> gem install rulebook
|
9
9
|
|
10
|
-
## _Notice_
|
11
|
-
|
12
|
-
The format is _back_ to what it was before 0.2.2!
|
13
|
-
|
14
10
|
## Simple Example
|
15
11
|
|
16
12
|
require 'rulebook'
|
17
13
|
|
18
14
|
class User
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
15
|
+
follows_the_rules!
|
16
|
+
|
17
|
+
def initialize(name)
|
18
|
+
@name = name
|
19
|
+
end
|
20
|
+
|
21
|
+
rulebook.add /say_(.+)/ do |what_to_say|
|
22
|
+
puts "#{@name} says '#{what_to_say.gsub(/_/, ' ')}'"
|
23
|
+
end
|
26
24
|
end
|
27
25
|
|
28
26
|
User.new('Ryan').say_hello_world # => Ryan says 'hello world'
|
29
27
|
|
30
28
|
## How It Works
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
When the first time `rule` is called in a class, we also include the `RuleBook::IncludeMethods` module which overrides the classes `method_missing`. So when you call an undefined method on your class's instance, we will try to match the method against the rules you've defined in `INSTANCE_RULEBOOK`.
|
35
|
-
|
36
|
-
There is also a method called `class_rule` which does the same as rules does, only it defines the `CLASS_RULEBOOK` constance in the class; which is a different `RuleBook` instance. The first time `class_rules` is called, it extends the class with the `RuleBook::ExtendMethods` module, which also contains a `method_missing` method.
|
30
|
+
TODO
|
37
31
|
|
38
32
|
## Better Example
|
39
33
|
|
40
34
|
require 'rulebook'
|
41
35
|
|
42
36
|
class User
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
37
|
+
attr :name, :title
|
38
|
+
|
39
|
+
def initialize(name)
|
40
|
+
@name = name
|
41
|
+
@title = :user
|
42
|
+
end
|
43
|
+
|
44
|
+
rulebook.add /is_(admin|moderator|super_user|user)/ do |title|
|
45
|
+
@title = title.to_sym
|
46
|
+
end
|
53
47
|
end
|
54
48
|
|
55
49
|
You can now do things like
|
56
50
|
|
57
|
-
users = [
|
58
|
-
|
59
|
-
User.new('Natale'),
|
60
|
-
User.new('Joe'),
|
61
|
-
User.new('Monica'),
|
62
|
-
User.new('Matt'),
|
63
|
-
User.new('Jess')
|
64
|
-
].shuffle
|
51
|
+
users = ['Ryan', 'Natale', 'Kasey', 'Jenna', 'Joe', 'Monica','Allan', 'Amanda']
|
52
|
+
users.collect! { |n| User.new(n) }.shuffle!
|
65
53
|
|
66
54
|
users[0].is_admin
|
67
55
|
users[1].is_moderator
|
68
56
|
users[2].is_super_user
|
69
57
|
|
70
58
|
users.each do |user|
|
71
|
-
|
59
|
+
puts "#{user.name} is a #{user.title}"
|
72
60
|
end
|
73
61
|
|
74
62
|
## Class Methods Example
|
@@ -76,26 +64,32 @@ You can now do things like
|
|
76
64
|
require 'rulebook'
|
77
65
|
|
78
66
|
class Car
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
67
|
+
attr :make, :model
|
68
|
+
|
69
|
+
def initialize(make, model)
|
70
|
+
@make, @model = make.capitalize, model.capitalize
|
71
|
+
end
|
72
|
+
|
73
|
+
class << self
|
74
|
+
follows_the_rules!
|
75
|
+
|
76
|
+
rulebook.add /new_([a-z]+)_(.+)/ do |make, model|
|
77
|
+
new(make, model)
|
87
78
|
end
|
79
|
+
end
|
88
80
|
end
|
89
81
|
|
90
82
|
my_cars = [
|
91
|
-
|
92
|
-
|
93
|
-
|
83
|
+
Car.new_honda_accord,
|
84
|
+
Car.new_dodge_neon,
|
85
|
+
Car.new_volkswagen_beetle
|
94
86
|
]
|
95
87
|
|
96
88
|
p my_cars.first.make # => "Honda"
|
97
89
|
p my_cars.first.model # => "Accord"
|
98
90
|
|
91
|
+
This works out if you already have ``
|
92
|
+
|
99
93
|
### Now lets add some instance rules
|
100
94
|
|
101
95
|
class Car
|
data/Rakefile
CHANGED
@@ -1,64 +1,43 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'rake'
|
3
2
|
|
4
|
-
|
5
|
-
require 'jeweler'
|
6
|
-
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name = "rulebook"
|
8
|
-
gem.summary = %Q{Allows you to define a set of 'rules' or dynamic methods to apply to a class}
|
9
|
-
gem.description = %Q{Lets you define methods with regex for dynamic methods}
|
10
|
-
gem.email = "c00lryguy@gmail.com"
|
11
|
-
gem.homepage = "http://github.com/c00lryguy/rulebook"
|
12
|
-
gem.authors = ["Ryan Lewis"]
|
13
|
-
# gem.add_development_dependency "riot", ">= 0"
|
14
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
-
end
|
16
|
-
Jeweler::GemcutterTasks.new
|
17
|
-
rescue LoadError
|
18
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
-
end
|
3
|
+
# Gay as hell jeweler workaround
|
20
4
|
|
21
|
-
require '
|
22
|
-
|
23
|
-
test.libs << 'lib' << 'test'
|
24
|
-
test.pattern = 'test/**/test_*.rb'
|
25
|
-
test.verbose = false
|
26
|
-
end
|
5
|
+
require 'psych'
|
6
|
+
YAML::ENGINE.yamler = 'psych'
|
27
7
|
|
8
|
+
require 'bundler'
|
28
9
|
begin
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
rescue LoadError
|
36
|
-
task :rcov do
|
37
|
-
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
-
end
|
10
|
+
Bundler.setup(:default, :development)
|
11
|
+
rescue Bundler::BundlerError => e
|
12
|
+
$stderr.puts e.message
|
13
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
14
|
+
exit e.status_code
|
39
15
|
end
|
16
|
+
require 'rake'
|
40
17
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
18
|
+
require 'jeweler'
|
19
|
+
Jeweler::Tasks.new do |gem|
|
20
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
21
|
+
gem.name = "rulebook"
|
22
|
+
gem.homepage = "http://github.com/c00lryguy/rulebook"
|
23
|
+
gem.license = "MIT"
|
24
|
+
gem.summary = %Q{Define methods with regex for dynamic methods.}
|
25
|
+
gem.description = %Q{Allows you to define a set of 'rules' or dynamic methods to apply to a class.}
|
26
|
+
gem.email = "c00lryguy@gmail.com"
|
27
|
+
gem.authors = ["Ryan Lewis"]
|
28
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
29
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
30
|
+
gem.add_runtime_dependency 'meta_tools', '> 0.1'
|
31
|
+
gem.add_development_dependency 'rake', '> 0.0.0'
|
32
|
+
gem.add_development_dependency 'riot', '> 0.0.0'
|
53
33
|
end
|
34
|
+
Jeweler::RubygemsDotOrgTasks.new
|
54
35
|
|
36
|
+
require 'rake/testtask'
|
55
37
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
63
|
-
rescue LoadError
|
64
|
-
end
|
38
|
+
Rake::TestTask.new(:test) do |t|
|
39
|
+
t.libs.concat ['lib', 'test']
|
40
|
+
t.pattern = 'test/**/test_*.rb'
|
41
|
+
t.verbose = false
|
42
|
+
end
|
43
|
+
task :default => :test
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
2
|
+
require 'rulebook'
|
3
|
+
|
4
|
+
class User
|
5
|
+
follows_the_rules!
|
6
|
+
|
7
|
+
attr :title
|
8
|
+
|
9
|
+
def initialize(title=:user)
|
10
|
+
@title = title
|
11
|
+
end
|
12
|
+
|
13
|
+
rulebook.add /^is_(admin|user)$/ do |title|
|
14
|
+
@title = title.to_sym
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
rulebook.add /^is_(admin|user)\?$/ do |title|
|
19
|
+
@title == title.to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
metaclass.follows_the_rules!
|
24
|
+
metaclass.rulebook.add /^new_(admin|user)$/ do |title|
|
25
|
+
instance = new
|
26
|
+
instance.instance_eval { @title = title.to_sym }
|
27
|
+
instance
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
u = User.new
|
33
|
+
|
34
|
+
p u.is_user? # => true
|
35
|
+
p u.is_admin? # => false
|
36
|
+
|
37
|
+
u.is_admin
|
38
|
+
|
39
|
+
p u.is_user? # => false
|
40
|
+
p u.is_admin? # => true
|
41
|
+
|
42
|
+
u = User.new_admin
|
43
|
+
p u.is_admin? # => true
|
data/lib/rulebook.rb
CHANGED
@@ -1,158 +1,19 @@
|
|
1
|
-
|
2
|
-
class Rule
|
3
|
-
attr :block
|
4
|
-
|
5
|
-
def initialize(what_to_capture, &block)
|
6
|
-
raise(TypeError, 'what_to_capture must be of type Regexp') unless what_to_capture.is_a?(Regexp)
|
7
|
-
@what_to_capture, @block = what_to_capture, block
|
8
|
-
end
|
9
|
-
|
10
|
-
def [](query)
|
11
|
-
query.to_s.match(@what_to_capture)
|
12
|
-
end
|
13
|
-
alias_method :match_against, :[]
|
14
|
-
|
15
|
-
def matches_against?(query)
|
16
|
-
!self[query].nil?
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
20
2
|
|
21
|
-
class
|
22
|
-
|
23
|
-
|
24
|
-
def initialize
|
25
|
-
@rules = []
|
26
|
-
end
|
27
|
-
|
28
|
-
def rule(what_to_capture, &block)
|
29
|
-
rule = Rule.new(what_to_capture, &block)
|
30
|
-
@rules << rule
|
31
|
-
rule
|
32
|
-
end
|
33
|
-
|
34
|
-
def rules_that_match_against(regexp)
|
35
|
-
@rules.find_all { |rule| rule.matches_against?(regexp) }
|
36
|
-
end
|
3
|
+
class Rulebook
|
4
|
+
VERSION = "0.4.0"
|
37
5
|
end
|
38
6
|
|
39
|
-
|
40
|
-
class RuleBook
|
41
|
-
module IncludeMethods
|
42
|
-
def respond_to?(meth)
|
43
|
-
rulebook = self.class.const_get('INSTANCE_RULEBOOK')
|
44
|
-
rulebook.rules_that_match_against(meth).any? || super
|
45
|
-
end
|
46
|
-
|
47
|
-
def method_missing(meth, *args, &block)
|
48
|
-
rulebook = self.class.const_get('INSTANCE_RULEBOOK')
|
49
|
-
rules = rulebook.rules_that_match_against(meth)
|
50
|
-
|
51
|
-
# TODO: Why would this ever be nil?
|
52
|
-
unless rules.nil? || rules.empty?
|
53
|
-
# Run the first matched rule..
|
54
|
-
# TODO: if the method NEXT if called within the rule,
|
55
|
-
# then goto the next matched rule
|
56
|
-
rule = rules.first
|
57
|
-
captures = rule[meth].captures || []
|
58
|
-
block = rule.block
|
59
|
-
|
60
|
-
# Remove the possibility of optional arguments
|
61
|
-
arity = block.arity == -1 ? 0 : block.arity
|
62
|
-
|
63
|
-
# Define the method
|
64
|
-
klass = self.class
|
65
|
-
klass.send(:define_method, meth) do |*args|
|
66
|
-
instance_exec(*(captures + args).take(arity), &block)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Call the method
|
70
|
-
send(meth, *args, &block)
|
71
|
-
else
|
72
|
-
super
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
module ExtendMethods
|
78
|
-
def respond_to?(meth)
|
79
|
-
rulebook = const_get('CLASS_NOTEBOOK')
|
80
|
-
rulebook.rules_that_match_against(meth).any? || super
|
81
|
-
end
|
82
|
-
|
83
|
-
def method_missing(meth, *args, &block)
|
84
|
-
rulebook = const_get('CLASS_NOTEBOOK')
|
85
|
-
rules = rulebook.rules_that_match_against(meth)
|
86
|
-
|
87
|
-
# TODO: Why would this ever be nil?
|
88
|
-
unless rules.nil? || rules.empty?
|
89
|
-
# Run the first matched rule..
|
90
|
-
# TODO: if the method NEXT if called within the rule,
|
91
|
-
# then goto the next matched rule
|
92
|
-
rule = rules.first
|
93
|
-
captures = rule[meth].captures || []
|
94
|
-
block = rule.block
|
95
|
-
|
96
|
-
# Remove the possibility of optional arguments
|
97
|
-
arity = block.arity == -1 ? 0 : block.arity
|
98
|
-
|
99
|
-
# Define the method
|
100
|
-
klass = class << self; self; end
|
101
|
-
klass.send(:define_method, meth) do |*args|
|
102
|
-
class_exec(*(captures + args).take(arity), &block)
|
103
|
-
end
|
104
|
-
|
105
|
-
# Call the method
|
106
|
-
send(meth, *args, &block)
|
107
|
-
else
|
108
|
-
super
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
7
|
+
require 'rulebook/rule'
|
113
8
|
|
114
|
-
|
115
|
-
|
116
|
-
def
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
const_get('INSTANCE_RULEBOOK').rule(what_to_capture, &block)
|
121
|
-
end
|
122
|
-
|
123
|
-
def class_rule(what_to_capture, &block)
|
124
|
-
raise(ArgumentError, 'class_rules must have a block') unless block_given?
|
125
|
-
|
126
|
-
setup_rulebook('CLASS_NOTEBOOK', :extend)
|
127
|
-
const_get('CLASS_NOTEBOOK').rule(what_to_capture, &block)
|
128
|
-
end
|
129
|
-
|
130
|
-
def rules(&block)
|
131
|
-
raise(ArgumentError, 'rules must have a block') unless block_given?
|
132
|
-
|
133
|
-
setup_rulebook('INSTANCE_RULEBOOK', :include)
|
134
|
-
const_get('INSTANCE_RULEBOOK').instance_eval(&block)
|
135
|
-
const_get('INSTANCE_RULEBOOK')
|
136
|
-
end
|
137
|
-
|
138
|
-
def class_rules(&block)
|
139
|
-
raise(ArgumentError, 'class_rules must have a block') unless block_given?
|
140
|
-
|
141
|
-
setup_rulebook('CLASS_NOTEBOOK', :extend)
|
142
|
-
const_get('CLASS_NOTEBOOK').instance_eval(&block)
|
143
|
-
const_get('CLASS_NOTEBOOK')
|
144
|
-
end
|
145
|
-
|
146
|
-
private
|
147
|
-
|
148
|
-
def setup_rulebook(rulebook_constant, extend_or_include)
|
149
|
-
raise(ArgumentError, 'extend_or_include must be :extend or :include') unless [:extend, :include].include?(extend_or_include)
|
150
|
-
|
151
|
-
unless const_defined?(rulebook_constant)
|
152
|
-
const_set(rulebook_constant, RuleBook.new)
|
153
|
-
|
154
|
-
module_name = extend_or_include.to_s.capitalize + 'Methods'
|
155
|
-
send(extend_or_include, RuleBook.const_get(module_name))
|
156
|
-
end
|
157
|
-
end
|
9
|
+
class Rulebook
|
10
|
+
attr_accessor :rules
|
11
|
+
def initialize; @rules = []; end
|
12
|
+
def add(what_to_capture, &block); @rules << Rule.new(what_to_capture, &block); end
|
13
|
+
def [](query); @rules.find_all { |rule| rule.matches_against?(query) }; end
|
14
|
+
alias_method :rules_that_match_against, :[]
|
158
15
|
end
|
16
|
+
|
17
|
+
require 'rulebook/class_methods'
|
18
|
+
require 'rulebook/instance_methods'
|
19
|
+
require 'rulebook/core_ext/module'
|