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 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