rulebook 0.1.1 → 0.2.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.
- data/README.md +84 -7
- data/TODO +2 -0
- data/VERSION +1 -1
- data/lib/rulebook.rb +46 -29
- data/rulebook.gemspec +7 -3
- data/test/test_chevy.rb +8 -5
- data/test/test_class_methods.rb +53 -0
- data/test/test_ryguy.rb +10 -9
- data/test/test_user.rb +17 -16
- metadata +8 -4
data/README.md
CHANGED
@@ -1,18 +1,47 @@
|
|
1
|
-
# rulebook
|
1
|
+
# rulebook (0.2.0)
|
2
2
|
|
3
|
-
Allows you to define a set of 'rules' or dynamic methods to apply to a class
|
3
|
+
Allows you to define a set of 'rules' or dynamic methods to apply to a class.
|
4
4
|
|
5
5
|
## Install
|
6
6
|
|
7
7
|
> gem update --system
|
8
8
|
> gem install rulebook
|
9
9
|
|
10
|
-
##
|
10
|
+
## _Notice_
|
11
|
+
|
12
|
+
The format has changed since version 0.1.1. Check out the example below:
|
13
|
+
|
14
|
+
## Simple Example
|
15
|
+
|
16
|
+
require 'rulebook'
|
17
|
+
|
18
|
+
class User
|
19
|
+
def initialize(name)
|
20
|
+
@name = name
|
21
|
+
end
|
22
|
+
|
23
|
+
rules do
|
24
|
+
rule /say_(.+)/ do |what_to_say|
|
25
|
+
puts "#{@name} says '#{what_to_say.gsub(/_/, ' ')}'"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
User.new('Ryan').say_hello_world # => "Ryan says 'hello world'"
|
31
|
+
|
32
|
+
## How It Works
|
33
|
+
|
34
|
+
When you call the `rules` method in a class, it defines the constant `INSTANCE_RULEBOOK` and sets it to a new `RuleBook` instance; but only if the constant wasn't already defined. This way, it only defines the constant the first time you call the `rules` method.
|
35
|
+
|
36
|
+
When the first time `rules` is called in a class, we also include the `RuleBook::InstanceMethods` 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`.
|
37
|
+
|
38
|
+
There is also a method called `class_rules` 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::ClassMethods` module, which also contains a `method_missing` method.
|
39
|
+
|
40
|
+
## Better Example
|
11
41
|
|
12
42
|
require 'rulebook'
|
13
43
|
|
14
44
|
class User
|
15
|
-
follows_rules
|
16
45
|
attr :name, :title
|
17
46
|
|
18
47
|
def initialize(name)
|
@@ -20,8 +49,10 @@ Allows you to define a set of 'rules' or dynamic methods to apply to a class
|
|
20
49
|
@title = :user
|
21
50
|
end
|
22
51
|
|
23
|
-
|
24
|
-
|
52
|
+
rules do
|
53
|
+
rule /is_(admin|moderator|super_user|user)/ do |title|
|
54
|
+
@title = title.to_sym
|
55
|
+
end
|
25
56
|
end
|
26
57
|
end
|
27
58
|
|
@@ -44,7 +75,49 @@ You can now do things like
|
|
44
75
|
puts "#{user.name} is a #{user.title}"
|
45
76
|
end
|
46
77
|
|
47
|
-
|
78
|
+
## Class Methods Example
|
79
|
+
|
80
|
+
class Car
|
81
|
+
attr :make, :model
|
82
|
+
|
83
|
+
def initialize(make, model)
|
84
|
+
@make, @model = make.capitalize, model.capitalize
|
85
|
+
end
|
86
|
+
|
87
|
+
class_rules do
|
88
|
+
rule /new_([a-z]+)_(.+)/ do |make, model|
|
89
|
+
new(make, model)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
my_cars = [
|
95
|
+
Car.new_honda_accord,
|
96
|
+
Car.new_dodge_neon,
|
97
|
+
Car.new_volkswagen_beetle
|
98
|
+
]
|
99
|
+
|
100
|
+
my_cars.first.make # => "Honda"
|
101
|
+
my_cars.first.model # => "Accord"
|
102
|
+
|
103
|
+
### Now lets add some instance rules
|
104
|
+
|
105
|
+
class Car
|
106
|
+
rules do
|
107
|
+
rule /is_(.+)\?/ do |make_or_model|
|
108
|
+
if @make == make_or_model || @model == make_or_model
|
109
|
+
true
|
110
|
+
else
|
111
|
+
false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
my_cars.first.is_honda? # => true
|
118
|
+
my_cars.first.is_beetle? # => false
|
119
|
+
|
120
|
+
#### There are more examples in the examples and [test][1] directories and [Rubular][2] is a great place to test your Regexp.
|
48
121
|
|
49
122
|
## Note on Patches/Pull Requests
|
50
123
|
|
@@ -59,3 +132,7 @@ There are more examples in the example directory and easy to understand tests in
|
|
59
132
|
## Copyright
|
60
133
|
|
61
134
|
Copyright (c) 2010 Ryan Lewis. See LICENSE for details.
|
135
|
+
|
136
|
+
|
137
|
+
[1]: http://github.com/c00lryguy/rulebook/tree/master/test/
|
138
|
+
[2]: http://rubular.com/
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/rulebook.rb
CHANGED
@@ -24,8 +24,10 @@ class RuleBook
|
|
24
24
|
@rules = []
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
|
27
|
+
def rule(what_to_capture, &block)
|
28
|
+
rule = Rule.new(what_to_capture, &block)
|
29
|
+
@rules << rule
|
30
|
+
rule
|
29
31
|
end
|
30
32
|
|
31
33
|
def find_rules_that_match_against(query)
|
@@ -34,40 +36,55 @@ class RuleBook
|
|
34
36
|
end
|
35
37
|
|
36
38
|
class RuleBook
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
module InstanceMethods
|
40
|
+
def method_missing(meth, *args, &blk)
|
41
|
+
rulebook = self.class.const_get('INSTANCE_RULEBOOK')
|
42
|
+
rules = rulebook.find_rules_that_match_against(meth)
|
43
|
+
|
44
|
+
unless rules.nil?
|
45
|
+
raise(ArgumentError, 'rules must have a block') unless block_given?
|
46
|
+
rule = rules.first
|
47
|
+
match = rule.match_against(meth)
|
48
|
+
instance_exec(*match.captures, *args, &rule.block)
|
49
|
+
else
|
50
|
+
super
|
44
51
|
end
|
45
52
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
53
|
+
end
|
54
|
+
|
55
|
+
module ClassMethods
|
56
|
+
def method_missing(meth, *args, &blk)
|
57
|
+
rulebook = const_get('CLASS_NOTEBOOK')
|
58
|
+
rules = rulebook.find_rules_that_match_against(meth)
|
59
|
+
|
60
|
+
unless rules.nil?
|
61
|
+
raise(ArgumentError, 'rules must have a block') unless block_given?
|
62
|
+
rule = rules.first
|
63
|
+
match = rule.match_against(meth)
|
64
|
+
class_exec(*match.captures, *args, &rule.block)
|
65
|
+
else
|
66
|
+
super
|
60
67
|
end
|
61
68
|
end
|
62
69
|
end
|
63
70
|
end
|
64
71
|
|
65
72
|
class Module
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
73
|
+
def rules(&blk)
|
74
|
+
unless const_defined?('INSTANCE_RULEBOOK')
|
75
|
+
const_set('INSTANCE_RULEBOOK', RuleBook.new)
|
76
|
+
include RuleBook::InstanceMethods
|
77
|
+
end
|
78
|
+
|
79
|
+
const_get('INSTANCE_RULEBOOK').instance_eval(&blk)
|
80
|
+
end
|
81
|
+
|
82
|
+
def class_rules(&blk)
|
83
|
+
unless const_defined?('CLASS_NOTEBOOK')
|
84
|
+
const_set('CLASS_NOTEBOOK', RuleBook.new)
|
85
|
+
extend RuleBook::ClassMethods
|
86
|
+
end
|
87
|
+
|
88
|
+
const_get('CLASS_NOTEBOOK').instance_eval(&blk)
|
72
89
|
end
|
73
90
|
end
|
data/rulebook.gemspec
CHANGED
@@ -5,16 +5,17 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rulebook}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ryan Lewis"]
|
12
|
-
s.date = %q{2010-04-
|
12
|
+
s.date = %q{2010-04-27}
|
13
13
|
s.description = %q{Lets you define methods with regex for dynamic methods}
|
14
14
|
s.email = %q{c00lryguy@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
"README.md"
|
17
|
+
"README.md",
|
18
|
+
"TODO"
|
18
19
|
]
|
19
20
|
s.files = [
|
20
21
|
".document",
|
@@ -22,11 +23,13 @@ Gem::Specification.new do |s|
|
|
22
23
|
"LICENSE",
|
23
24
|
"README.md",
|
24
25
|
"Rakefile",
|
26
|
+
"TODO",
|
25
27
|
"VERSION",
|
26
28
|
"lib/rulebook.rb",
|
27
29
|
"rulebook.gemspec",
|
28
30
|
"test/helper.rb",
|
29
31
|
"test/test_chevy.rb",
|
32
|
+
"test/test_class_methods.rb",
|
30
33
|
"test/test_ryguy.rb",
|
31
34
|
"test/test_user.rb"
|
32
35
|
]
|
@@ -38,6 +41,7 @@ Gem::Specification.new do |s|
|
|
38
41
|
s.test_files = [
|
39
42
|
"test/helper.rb",
|
40
43
|
"test/test_chevy.rb",
|
44
|
+
"test/test_class_methods.rb",
|
41
45
|
"test/test_ryguy.rb",
|
42
46
|
"test/test_user.rb"
|
43
47
|
]
|
data/test/test_chevy.rb
CHANGED
@@ -2,15 +2,16 @@ require 'helper'
|
|
2
2
|
|
3
3
|
class TestChevy < Test::Unit::TestCase
|
4
4
|
class Engine
|
5
|
-
follows_rules
|
6
5
|
attr :state
|
7
6
|
|
8
7
|
def initialize
|
9
8
|
@state = "off"
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
|
-
|
11
|
+
rules do
|
12
|
+
rule(/is_(.*)/) do |state|
|
13
|
+
@state = state.gsub(/_/, " ")
|
14
|
+
end
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
@@ -43,8 +44,10 @@ class TestChevy < Test::Unit::TestCase
|
|
43
44
|
setup do
|
44
45
|
@chevy = Engine.new
|
45
46
|
class << @chevy
|
46
|
-
|
47
|
-
|
47
|
+
rules do
|
48
|
+
rule(/is_(.*)?/) do |state|
|
49
|
+
@state == state
|
50
|
+
end
|
48
51
|
end
|
49
52
|
end
|
50
53
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestClassMethods < Test::Unit::TestCase
|
4
|
+
class Car
|
5
|
+
attr :make, :model
|
6
|
+
|
7
|
+
def initialize(make, model)
|
8
|
+
@make, @model = make, model
|
9
|
+
end
|
10
|
+
|
11
|
+
class_rules do
|
12
|
+
rule(/new_([a-z]+)_(.+)/) do |make, model|
|
13
|
+
|
14
|
+
make.capitalize!
|
15
|
+
model = model.split('_').inject(''){ |result, word|
|
16
|
+
result << word.capitalize + ' '
|
17
|
+
}.strip
|
18
|
+
new(make, model)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'be a Ford F150' do
|
24
|
+
@car = Car.new_ford_f150
|
25
|
+
assert_equal @car.make, 'Ford'
|
26
|
+
assert_equal @car.model, 'F150'
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'be a Ford Mustang' do
|
30
|
+
@car = Car.new_ford_mustang
|
31
|
+
assert_equal @car.make, 'Ford'
|
32
|
+
assert_equal @car.model, 'Mustang'
|
33
|
+
end
|
34
|
+
|
35
|
+
should 'be a Pontiac GTO' do
|
36
|
+
@car = Car.new_pontiac_gto
|
37
|
+
@car.model.upcase!
|
38
|
+
assert_equal @car.make, 'Pontiac'
|
39
|
+
assert_equal @car.model, 'GTO'
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'be a Toyota Camry' do
|
43
|
+
@car = Car.new_toyota_camry
|
44
|
+
assert_equal @car.make, 'Toyota'
|
45
|
+
assert_equal @car.model, 'Camry'
|
46
|
+
end
|
47
|
+
|
48
|
+
should 'be a Hyundai Santa Fe' do
|
49
|
+
@car = Car.new_hyundai_santa_fe
|
50
|
+
assert_equal @car.make, 'Hyundai'
|
51
|
+
assert_equal 'Santa Fe', @car.model
|
52
|
+
end
|
53
|
+
end
|
data/test/test_ryguy.rb
CHANGED
@@ -2,17 +2,18 @@ require 'helper'
|
|
2
2
|
|
3
3
|
class TestRyguy < Test::Unit::TestCase
|
4
4
|
class Ryguy
|
5
|
-
follows_rules
|
6
5
|
attr :nouns, :adjectives
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
rules do
|
8
|
+
rule(/is_a_(.*)/) do |noun|
|
9
|
+
@nouns ||= []
|
10
|
+
@nouns << noun.gsub(/_/, ' ')
|
11
|
+
end
|
12
|
+
|
13
|
+
rule(/is_(.*)/) do |adjective|
|
14
|
+
@adjectives ||= []
|
15
|
+
@adjectives << adjective.gsub(/_/, ' ')
|
16
|
+
end
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
data/test/test_user.rb
CHANGED
@@ -3,24 +3,25 @@ require 'date'
|
|
3
3
|
|
4
4
|
class TestUser < Test::Unit::TestCase
|
5
5
|
class User
|
6
|
-
follows_rules
|
7
6
|
attr :gender, :height, :eye_color, :date_of_birth, :age
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
8
|
+
rules do
|
9
|
+
rule(/is_(male|female)/) do |gender|
|
10
|
+
@gender = gender.to_sym
|
11
|
+
end
|
12
|
+
|
13
|
+
rule(/is_(\d+)_foot_(\d+)(_inches)?/) do |feet, inches|
|
14
|
+
@height = "#{feet}-#{inches}"
|
15
|
+
end
|
16
|
+
|
17
|
+
rule(/has_(amber|blue|brown|gray|grey|green|hazel|red)_eyes/) do |eye_color|
|
18
|
+
@eye_color = eye_color.to_sym
|
19
|
+
end
|
20
|
+
|
21
|
+
rule(/was_born_([a-z]+_\d+(st|nd|rd|th)_\d+)/) do |date_of_birth|
|
22
|
+
@date_of_birth = Date.parse(date_of_birth.gsub(/_/, ' '))
|
23
|
+
@age = (Date.today - @date_of_birth).to_i / 365
|
24
|
+
end
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ryan Lewis
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-27 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -27,17 +27,20 @@ extensions: []
|
|
27
27
|
extra_rdoc_files:
|
28
28
|
- LICENSE
|
29
29
|
- README.md
|
30
|
+
- TODO
|
30
31
|
files:
|
31
32
|
- .document
|
32
33
|
- .gitignore
|
33
34
|
- LICENSE
|
34
35
|
- README.md
|
35
36
|
- Rakefile
|
37
|
+
- TODO
|
36
38
|
- VERSION
|
37
39
|
- lib/rulebook.rb
|
38
40
|
- rulebook.gemspec
|
39
41
|
- test/helper.rb
|
40
42
|
- test/test_chevy.rb
|
43
|
+
- test/test_class_methods.rb
|
41
44
|
- test/test_ryguy.rb
|
42
45
|
- test/test_user.rb
|
43
46
|
has_rdoc: true
|
@@ -73,5 +76,6 @@ summary: Allows you to define a set of 'rules' or dynamic methods to apply to a
|
|
73
76
|
test_files:
|
74
77
|
- test/helper.rb
|
75
78
|
- test/test_chevy.rb
|
79
|
+
- test/test_class_methods.rb
|
76
80
|
- test/test_ryguy.rb
|
77
81
|
- test/test_user.rb
|