rulebook 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
- ## Example
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
- rule /is_(admin|moderator|super_user|user)/ do |title|
24
- @title = title.to_sym
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
- There are more examples in the example directory and easy to understand tests in the tests directory
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/TODO ADDED
@@ -0,0 +1,2 @@
1
+ # Support for class methods
2
+ # Named captures in Regexp
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
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 add_rule(what_to_capture, &block)
28
- @rules << Rule.new(what_to_capture, &block)
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
- # Provides the ClassMethods and InstanceMethods modules that get mixed into
38
- # the class that #follows_rules is called in
39
- module Mixin
40
- module ClassMethods
41
- def rule(what_to_capture, &block)
42
- rulebook = const_get('RULEBOOK')
43
- rulebook.add_rule(what_to_capture, &block)
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
- module InstanceMethods
48
- def method_missing(meth, *args, &blk)
49
- rulebook = self.class.const_get('RULEBOOK')
50
- rules = rulebook.find_rules_that_match_against(meth)
51
-
52
- unless rules.nil?
53
- rules.first.tap do |rule|
54
- match = rule.match_against(meth)
55
- instance_exec(*match.captures, *args, &rule.block)
56
- end
57
- else
58
- super
59
- end
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
- # Mixes in RuleBook::Mixin::ClassMethods and RuleBook::Mixin::InstanceMethods
67
- # TODO: allow argument to use other RuleBook instances.. also multiple rulebooks
68
- def follows_rules
69
- const_set('RULEBOOK', RuleBook.new)
70
- extend RuleBook::Mixin::ClassMethods
71
- include RuleBook::Mixin::InstanceMethods
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.1.1"
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-25}
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
- rule(/is_(.*)/) do |state|
13
- @state = state.gsub(/_/, " ")
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
- rule(/is_(.*)?/) do |state|
47
- @state == state
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
- 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(/_/, ' ')
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
- 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
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
- - 1
8
- - 1
9
- version: 0.1.1
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-25 00:00:00 -04:00
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