perennial 1.0.0.2 → 1.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/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # Perennial - a Ruby Event-driven application Library #
2
+
3
+ Perennial is yet another god damned event library / framework
4
+ for Ruby, built on top of EventMachine. The whole goal of Perennial
5
+ is to make it easy to build a particular style / class of application
6
+ such as [Marvin](http://github.com/Sutto/marvin). Most of the code
7
+ has been extracted from building Marvin (and later, BirdGrinder - like
8
+ Marvin but for twitter).
9
+
10
+ Applications built for Perennial are devised around the concept of 'clients'
11
+ and 'handlers'. Clients are things which handle the actual processing (e.g.
12
+ Marvin's IRC client takes the IRC protocol and converts it into events) and
13
+ handlers respond to messages. In practice, it's an approach inspired by Rack
14
+ in that we do the simplest thing's possible.
15
+
16
+ Since each event processed by a handler is incredibly simple (a symbol for a name
17
+ and a hash of associated options / details), and there are typically few requirements
18
+ (e.g. a handler only typically needs to define the 'handle' method) it
19
+ makes it relatively easy to build your application how you like.
20
+
21
+ In other words, Perennial is mainly a bunch of useful mixings (Dispatchable,
22
+ Hookable, Delegateable) which fit in with some evented application design
23
+ along with the framework for building applications around this design.
@@ -59,6 +59,8 @@ module Perennial
59
59
  Logger.debug "Dispatching #{name} event (#{dispatch_queue.size} queued - on #{self.class.name})"
60
60
  # Add ourselves to the queue
61
61
  @dispatching = true
62
+ # TODO: improve performance. This should be dispatched per-request cycle.
63
+ pre_dispatching
62
64
  begin
63
65
  # The full handler name is the method we call given it exists.
64
66
  full_handler_name = :"handle_#{name.to_s.underscore}"
@@ -88,13 +90,20 @@ module Perennial
88
90
  Logger.log_exception(e)
89
91
  end
90
92
  @dispatching = false
91
- dispatch(*@dispatch_queue.shift) unless dispatch_queue.empty?
93
+ dispatch(*@dispatch_queue.shift) if dispatch_queue.present?
94
+ post_dispatching
92
95
  else
93
96
  Logger.debug "Adding #{name} event to the end of the queue (on #{self.class.name})"
94
97
  dispatch_queue << [name, opts]
95
98
  end
96
99
  end
97
100
 
101
+ def pre_dispatching
102
+ end
103
+
104
+ def post_dispatching
105
+ end
106
+
98
107
  end
99
108
 
100
109
  module ClassMethods
@@ -0,0 +1,75 @@
1
+ module Perennial
2
+ module JewelerExt
3
+ # Adds in utility messages to make it easier to manage versioning.
4
+
5
+ class << self
6
+ attr_accessor :version_array, :library_file
7
+ end
8
+
9
+ module Versioning
10
+
11
+ def write
12
+ contents = File.read(main_library_file)
13
+ contents.gsub!(/(VERSION\s+=\s+)(\[\d+\,\s*\d+\,\s*\d+(?:\,\s*\d+)?\])/) do |m|
14
+ "#{$1}#{array.inspect}"
15
+ end
16
+ File.open(main_library_file, "w+") do |f|
17
+ f.write(contents)
18
+ end
19
+ end
20
+
21
+ def array
22
+ [@major, @minor, @patch, @build].compact
23
+ end
24
+
25
+ def to_s
26
+ items = array
27
+ items.pop if items[3] == 0
28
+ items.join(".")
29
+ end
30
+
31
+ def main_library_file
32
+ Perennial::JewelerExt.library_file
33
+ end
34
+
35
+ def parse_version
36
+ parts = Perennial::JewelerExt.version_array
37
+ @major = parts[0]
38
+ @minor = parts[1]
39
+ @patch = parts[2]
40
+ @build = parts[3]
41
+ end
42
+
43
+ def refresh
44
+ parse_version
45
+ end
46
+
47
+ def path
48
+ nil
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+
55
+ require 'jeweler' unless defined?(Jeweler)
56
+
57
+ # Overrides Jeweler to version using our stuff
58
+
59
+ def Jeweler.versioning_via(file, version_array)
60
+ Perennial::JewelerExt.library_file = file
61
+ Perennial::JewelerExt.version_array = version_array
62
+ # Extend with the Perennial extensions
63
+ Jeweler::VersionHelper.class_eval do
64
+ def initialize(base_dir)
65
+ extend Perennial::JewelerExt::Versioning
66
+ parse_version
67
+ end
68
+ end
69
+ # Ensure version exists
70
+ Jeweler.class_eval do
71
+ def version_exists?
72
+ true
73
+ end
74
+ end
75
+ end
data/lib/perennial.rb CHANGED
@@ -9,7 +9,7 @@ require 'perennial/exceptions'
9
9
 
10
10
  module Perennial
11
11
 
12
- VERSION = "1.0.0.2"
12
+ VERSION = [1, 0, 1, 0]
13
13
 
14
14
  has_library :dispatchable, :hookable, :loader, :logger, :nash,
15
15
  :loggable, :manifest, :settings, :argument_parser,
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class DelegateableTest < Test::Unit::TestCase
4
+
5
+ context 'basic delegates' do
6
+
7
+ setup do
8
+ @klass = test_class_for(Perennial::Delegateable)
9
+ @delegateable = @klass.new
10
+ end
11
+
12
+ should 'define a delegate= method' do
13
+ assert @delegateable.respond_to?(:delegate=)
14
+ end
15
+
16
+ should 'define a delegate_to method' do
17
+ assert @delegateable.respond_to?(:delegate_to)
18
+ end
19
+
20
+ should 'let you get the delegate proxy' do
21
+ @delegateable.delegate_to :awesome
22
+ assert proxy = @delegateable.delegate
23
+ assert_nothing_raised { proxy.awesomesauce }
24
+ assert_nothing_raised { proxy.ninja_party }
25
+ assert_equal "awesome", proxy.to_s
26
+ end
27
+
28
+ should 'let you get the real target of the delegate' do
29
+ @delegateable.delegate_to :awesome
30
+ assert real = @delegateable.real_delegate
31
+ assert_raises(NoMethodError) { proxy.awesomesauce }
32
+ assert_raises(NoMethodError) { proxy.ninja_party }
33
+ assert_equal "awesome", real.to_s
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,212 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class DispatchableTest < Test::Unit::TestCase
4
+
5
+ class ExampleDispatcher
6
+ include Perennial::Dispatchable
7
+ end
8
+
9
+ class ExampleHandlerA
10
+
11
+ attr_accessor :messages
12
+
13
+ def initialize
14
+ @messages = []
15
+ end
16
+
17
+ def handle(name, opts)
18
+ @messages << [name, opts]
19
+ end
20
+
21
+ end
22
+
23
+ class ExampleHandlerB < ExampleHandlerA; end
24
+
25
+ class ExampleHandlerC
26
+ attr_reader :max_count, :messages, :call_stack
27
+ def initialize(d)
28
+ @messages = []
29
+ @max_count = 0
30
+ @current_count = 0
31
+ @dispatcher = d
32
+ @call_stack = []
33
+ end
34
+
35
+ def handle(name, opts)
36
+ @call_stack << "start-#{name}"
37
+ @current_count += 1
38
+ @messages << name
39
+ @dispatcher.dispatch(:b) if name == :a
40
+ @max_count = @current_count if @current_count > @max_count
41
+ @current_count -= 1
42
+ @call_stack << "end-#{name}"
43
+ end
44
+
45
+ end
46
+
47
+ class RegisterableHandler
48
+
49
+ def registered=(value)
50
+ @registered = value
51
+ end
52
+
53
+ def registered?
54
+ @registered ||= false
55
+ end
56
+
57
+ def handle(name, opts = {})
58
+ end
59
+
60
+ end
61
+
62
+ context 'marking a class as dispatchable' do
63
+
64
+ setup do
65
+ @dispatcher = ExampleDispatcher.new
66
+ end
67
+
68
+ should 'define a dispatch method' do
69
+ assert @dispatcher.respond_to?(:dispatch)
70
+ end
71
+
72
+ should 'require atleast a name for dispatch' do
73
+ assert_equal -2, @dispatcher.method(:dispatch).arity
74
+ end
75
+
76
+ end
77
+
78
+ context 'when registering handlers' do
79
+
80
+ setup do
81
+ @dispatcher = test_class_for(Perennial::Dispatchable)
82
+ end
83
+
84
+ should 'append a handler using register_handler' do
85
+ assert_equal [], @dispatcher.handlers
86
+ @dispatcher.register_handler(handler = ExampleHandlerA.new)
87
+ assert_equal [handler], @dispatcher.handlers
88
+ end
89
+
90
+ should 'batch assign handlers on handlers= using register_handler' do
91
+ handlers = [ExampleHandlerA.new, ExampleHandlerB.new]
92
+ assert_equal [], @dispatcher.handlers
93
+ @dispatcher.handlers = handlers
94
+ assert_equal handlers, @dispatcher.handlers
95
+ end
96
+
97
+ should 'return all handlers via the handlers class method' do
98
+ handlers = [ExampleHandlerA.new, ExampleHandlerB.new]
99
+ @dispatcher.handlers = handlers
100
+ assert_equal handlers, @dispatcher.handlers
101
+ end
102
+
103
+ should 'make handlers available to myself and all subclasses' do
104
+ # Set A
105
+ dispatcher_a = class_via(@dispatcher)
106
+ dispatcher_a.register_handler(handler_a = ExampleHandlerA.new)
107
+ # Set B
108
+ dispatcher_b = class_via(dispatcher_a)
109
+ dispatcher_b.register_handler(handler_b = ExampleHandlerA.new)
110
+ # Set C
111
+ dispatcher_c = class_via(dispatcher_b)
112
+ dispatcher_c.register_handler(handler_c = ExampleHandlerA.new)
113
+ # Set D
114
+ dispatcher_d = class_via(dispatcher_a)
115
+ dispatcher_d.register_handler(handler_d = ExampleHandlerB.new)
116
+ # Actual Assertions
117
+ assert_equal [], @dispatcher.handlers
118
+ assert_equal [handler_a], dispatcher_a.handlers
119
+ assert_equal [handler_a, handler_b], dispatcher_b.handlers
120
+ assert_equal [handler_a, handler_b, handler_c], dispatcher_c.handlers
121
+ assert_equal [handler_a, handler_d], dispatcher_d.handlers
122
+ end
123
+
124
+ end
125
+
126
+ context 'dispatching events' do
127
+
128
+ setup do
129
+ @dispatcher = class_via(ExampleDispatcher).new
130
+ @handler = ExampleHandlerA.new
131
+ @dispatcher.class.register_handler @handler
132
+ end
133
+
134
+ should 'attempt to call handle_[event_name] on itself' do
135
+ mock(@dispatcher).respond_to?(:handle_sample_event) { true }
136
+ mock(@dispatcher).handle_sample_event(:awesome => true, :sauce => 2)
137
+ @dispatcher.dispatch :sample_event, :awesome => true, :sauce => 2
138
+ end
139
+
140
+ should 'attempt to call handle_[event_name] on each handler' do
141
+ mock(@handler).respond_to?(:handle_sample_event) { true }
142
+ mock(@handler).handle_sample_event(:awesome => true, :sauce => 2)
143
+ @dispatcher.dispatch :sample_event, :awesome => true, :sauce => 2
144
+ end
145
+
146
+ should 'call handle on each handler if handle_[event_name] isn\'t defined' do
147
+ mock(@handler).respond_to?(:handle_sample_event) { false }
148
+ mock(@handler).handle(:sample_event, :awesome => true, :sauce => 2)
149
+ @dispatcher.dispatch :sample_event, :awesome => true, :sauce => 2
150
+ end
151
+
152
+ should 'let you halt handler processing if you raise HaltHandlerProcessing' do
153
+ handler_two = ExampleHandlerB.new
154
+ @dispatcher.class.register_handler handler_two
155
+ mock(@handler).handle(:sample_event, :awesome => true, :sauce => 2) do
156
+ raise Perennial::HaltHandlerProcessing
157
+ end
158
+ dont_allow(handler_two).handle(:sample_event, :awesome => true, :sauce => 2)
159
+ @dispatcher.dispatch :sample_event, :awesome => true, :sauce => 2
160
+ end
161
+
162
+ should 'log exceptions when encountered and not crash'
163
+
164
+ end
165
+
166
+ context 'dispatching nested events' do
167
+
168
+ setup do
169
+ @dispatcher = class_via(ExampleDispatcher).new
170
+ @handler = ExampleHandlerC.new(@dispatcher)
171
+ @dispatcher.class.register_handler @handler
172
+ @dispatcher.dispatch :a
173
+ end
174
+
175
+ should 'call them in the correct order' do
176
+ assert_equal [:a, :b], @handler.messages
177
+ end
178
+
179
+ should 'only call 1 dispatch at a time' do
180
+ assert_equal 1, @handler.max_count
181
+ end
182
+
183
+ should 'finish a before dispatching b' do
184
+ assert_equal ["start-a", "end-a", "start-b", "end-b"], @handler.call_stack
185
+ end
186
+
187
+ end
188
+
189
+ context 'registering handlers' do
190
+
191
+ setup do
192
+ @dispatcher = class_via(ExampleDispatcher).new
193
+ @handler = class_via(RegisterableHandler).new
194
+ end
195
+
196
+ should 'default to not being registered' do
197
+ assert !@handler.registered?
198
+ end
199
+
200
+ should 'set registered on register_handler' do
201
+ @dispatcher.class.register_handler @handler
202
+ assert @handler.registered?
203
+ end
204
+
205
+ should 'call registered= on the handler' do
206
+ mock(@handler).registered = true
207
+ @dispatcher.class.register_handler @handler
208
+ end
209
+
210
+ end
211
+
212
+ end
@@ -0,0 +1,61 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class HookableTest < Test::Unit::TestCase
4
+
5
+ context 'Hookable Classes' do
6
+
7
+ setup do
8
+ @hookable_class = test_class_for(Perennial::Hookable)
9
+ end
10
+
11
+ should 'let you append hooks via append_hook' do
12
+ assert_equal [], @hookable_class.hooks_for(:awesome)
13
+ @hookable_class.append_hook(:awesome) { puts "Hello!" }
14
+ assert_equal 1, @hookable_class.hooks_for(:awesome).size
15
+ end
16
+
17
+ should 'only append hooks if they aren\'t blank' do
18
+ @hookable_class.append_hook(:awesome)
19
+ assert_equal [], @hookable_class.hooks_for(:awesome)
20
+ end
21
+
22
+ should 'let you get an array of hooks' do
23
+ @hookable_class.append_hook(:awesome) { puts "A" }
24
+ @hookable_class.append_hook(:awesome) { puts "B" }
25
+ assert_equal 2, @hookable_class.hooks_for(:awesome).size
26
+ end
27
+
28
+ should 'let you invoke hooks' do
29
+ items = []
30
+ @hookable_class.append_hook(:awesome) { items << :a }
31
+ @hookable_class.append_hook(:awesome) { items << :b }
32
+ @hookable_class.append_hook(:awesome) { items << :c }
33
+ @hookable_class.invoke_hooks!(:awesome)
34
+ assert_equal [:a, :b, :c], items
35
+ end
36
+
37
+ should 'call them in the order they are appended' do
38
+ items = []
39
+ @hookable_class.append_hook(:awesome) { items << :a }
40
+ @hookable_class.append_hook(:awesome) { items << :b }
41
+ @hookable_class.append_hook(:awesome) { items << :c }
42
+ @hookable_class.invoke_hooks!(:awesome)
43
+ [:a, :b, :c].each_with_index do |value, index|
44
+ assert_equal value, items[index]
45
+ end
46
+ end
47
+
48
+ should 'let you define hook accessors' do
49
+ assert_equal [], @hookable_class.hooks_for(:awesome)
50
+ assert !@hookable_class.respond_to?(:awesome)
51
+ assert !@hookable_class.respond_to?(:sauce)
52
+ @hookable_class.define_hook :awesome, :sauce
53
+ assert @hookable_class.respond_to?(:awesome)
54
+ assert @hookable_class.respond_to?(:sauce)
55
+ @hookable_class.awesome { puts "A" }
56
+ assert_equal 1, @hookable_class.hooks_for(:awesome).size
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class LoggableTest < Test::Unit::TestCase
4
+
5
+ class ExampleLoggable
6
+ include Perennial::Loggable
7
+ end
8
+
9
+ context "Defining a class as loggable" do
10
+
11
+ setup do
12
+ @example = ExampleLoggable.new
13
+ end
14
+
15
+ should 'define a logger instance method' do
16
+ assert @example.respond_to?(:logger)
17
+ end
18
+
19
+ should 'define a logger class method' do
20
+ assert ExampleLoggable.respond_to?(:logger)
21
+ end
22
+
23
+ should 'not define a logger= instance method' do
24
+ assert !@example.respond_to?(:logger=)
25
+ end
26
+
27
+ should 'not define a logger= class method' do
28
+ assert !ExampleLoggable.respond_to?(:logger=)
29
+ end
30
+
31
+ should 'define logger to be an instance of Perennial::Logger' do
32
+ assert_equal Perennial::Logger, ExampleLoggable.logger
33
+ assert_equal Perennial::Logger, @example.logger
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,59 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class LoggerTest < Test::Unit::TestCase
4
+ context 'logger tests' do
5
+
6
+ setup do
7
+ @root_path = Perennial::Settings.root / "log"
8
+ Perennial::Logger.log_name = "example.log"
9
+ FileUtils.mkdir_p @root_path
10
+ end
11
+
12
+ context 'setting up a logger' do
13
+
14
+ setup { Perennial::Logger.setup! }
15
+
16
+ should 'create the log file file after writing' do
17
+ Perennial::Logger.fatal "Blergh."
18
+ assert File.exist?(@root_path / "example.log")
19
+ end
20
+
21
+ Perennial::Logger::LEVELS.each_key do |level_name|
22
+ should "define a method for the #{level_name} log level" do
23
+ assert Perennial::Logger.respond_to?(level_name)
24
+ assert Perennial::Logger.logger.respond_to?(level_name)
25
+ assert_equal 1, Perennial::Logger.logger.method(level_name).arity
26
+ end
27
+ end
28
+
29
+ should 'have a log exception method' do
30
+ assert Perennial::Logger.respond_to?(:log_exception)
31
+ assert Perennial::Logger.logger.respond_to?(:log_exception)
32
+ end
33
+
34
+ should 'let you configure a dir that logs are loaded from'
35
+
36
+ end
37
+
38
+ context 'writing to the log' do
39
+
40
+ Perennial::Logger::LEVELS.each_key do |level_name|
41
+ should "let you write to the #{level_name} log level" do
42
+ Perennial::Logger.verbose = false
43
+ Perennial::Logger.level = level_name
44
+ assert_nothing_raised do
45
+ Perennial::Logger.logger.send(level_name, "An Example Message No. 1")
46
+ end
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ teardown do
53
+ log_path = @root_path / "example.log"
54
+ FileUtils.rm_rf(log_path) if File.exist?(log_path)
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,30 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class ProxyTest < Test::Unit::TestCase
4
+
5
+ context 'basic proxies' do
6
+
7
+ setup do
8
+ @proxy = Perennial::Proxy.new
9
+ @proxy.__proxy_target__ = :awesome
10
+ end
11
+ should 'pass through the correct class' do
12
+ assert_equal Symbol, @proxy.class
13
+ assert_kind_of Symbol, @proxy
14
+ end
15
+
16
+ should 'not interfere with equals' do
17
+ assert @proxy == :awesome
18
+ end
19
+
20
+ should 'pass through to_s' do
21
+ assert_equal "awesome", @proxy.to_s
22
+ end
23
+
24
+ should 'let you send to an object' do
25
+ assert_equal "awesome", @proxy.send(:to_s)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,103 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class SettingsTest < Test::Unit::TestCase
4
+
5
+ context 'default settings' do
6
+
7
+ setup do
8
+ Perennial::Settings.setup!
9
+ end
10
+
11
+ should "default the application root to the parent folder of perennial" do
12
+ assert_equal __FILE__.to_pathname.dirname.join("..").expand_path,
13
+ Perennial::Settings.root.to_pathname
14
+ Perennial::Settings.root = "/awesome/sauce"
15
+ assert_equal "/awesome/sauce", Perennial::Settings.root
16
+ end
17
+
18
+ should "default daemonized to false" do
19
+ assert !Perennial::Settings.daemon?
20
+ Perennial::Settings.daemon = true
21
+ assert Perennial::Settings.daemon?
22
+ Perennial::Settings.daemon = false
23
+ assert !Perennial::Settings.daemon?
24
+ end
25
+
26
+ should "default the log level to :info" do
27
+ assert_equal :info, Perennial::Settings.log_level
28
+ Perennial::Settings.log_level = :debug
29
+ assert_equal :debug, Perennial::Settings.log_level
30
+ end
31
+
32
+ should "default verbose to false" do
33
+ assert !Perennial::Settings.verbose?
34
+ Perennial::Settings.verbose = true
35
+ assert Perennial::Settings.verbose?
36
+ Perennial::Settings.verbose = false
37
+ assert !Perennial::Settings.verbose?
38
+ end
39
+
40
+ end
41
+
42
+ context 'loading settings' do
43
+
44
+ setup do
45
+ config_folder = Perennial::Settings.root / "config"
46
+ @default_settings = {
47
+ "default" => {
48
+ "introduction" => true,
49
+ "description" => "Ninjas are Totally Awesome",
50
+ "channel" => "#offrails",
51
+ "users" => ["Sutto", "njero", "zapnap"]
52
+ }
53
+ }
54
+ FileUtils.mkdir_p(config_folder)
55
+ File.open(config_folder / "settings.yml", "w+") do |file|
56
+ file.write(@default_settings.to_yaml)
57
+ end
58
+ Perennial::Settings.setup!
59
+ end
60
+
61
+ should 'load settings from the file' do
62
+ assert Perennial::Settings.setup?
63
+ assert_equal @default_settings["default"].symbolize_keys, Perennial::Settings.to_hash
64
+ end
65
+
66
+ should 'define readers for the settings' do
67
+ instance = Perennial::Settings.new
68
+ @default_settings["default"].each_pair do |key, value|
69
+ assert Perennial::Settings.respond_to?(key.to_sym)
70
+ assert_equal value, Perennial::Settings.send(key)
71
+ assert instance.respond_to?(key.to_sym)
72
+ assert_equal value, instance.send(key)
73
+ end
74
+ end
75
+
76
+ should 'let you access settings via hash-style accessors' do
77
+ @default_settings["default"].each_pair do |key, value|
78
+ assert_equal value, Perennial::Settings[key]
79
+ Perennial::Settings[key] = "a-new-value from #{value.inspect}"
80
+ assert_equal "a-new-value from #{value.inspect}", Perennial::Settings[key]
81
+ end
82
+ end
83
+
84
+ should 'define writers for the settings' do
85
+ instance = Perennial::Settings.new
86
+ @default_settings["default"].each_pair do |key, value|
87
+ setter = :"#{key}="
88
+ assert Perennial::Settings.respond_to?(setter)
89
+ Perennial::Settings.send(setter, "value #{value.inspect} on class")
90
+ assert_equal "value #{value.inspect} on class", Perennial::Settings.send(key)
91
+ assert instance.respond_to?(setter)
92
+ instance.send(setter, "value #{value.inspect} on instance")
93
+ assert_equal "value #{value.inspect} on instance", instance.send(key)
94
+ end
95
+ end
96
+
97
+ should 'let you configure the lookup key path'
98
+
99
+ should 'let you configure the file settings are loaded from'
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,38 @@
1
+ require 'rubygems'
2
+
3
+ # Testing dependencies
4
+ require 'test/unit'
5
+ require 'shoulda'
6
+ require 'rr'
7
+ # RedGreen doesn't seem to be needed under 1.9
8
+ require 'redgreen' if RUBY_VERSION < "1.9"
9
+
10
+ require 'pathname'
11
+ root_directory = Pathname.new(__FILE__).dirname.join("..").expand_path
12
+ require root_directory.join("lib", "perennial")
13
+ require root_directory.join("vendor", "fakefs", "lib", "fakefs")
14
+
15
+ class Test::Unit::TestCase
16
+ include RR::Adapters::TestUnit
17
+
18
+ protected
19
+
20
+ # Short hand for creating a class with
21
+ # a given class_eval block.
22
+ def class_via(*args, &blk)
23
+ klass = Class.new(*args)
24
+ klass.class_eval(&blk) unless blk.blank?
25
+ return klass
26
+ end
27
+
28
+ # Short hand for creating a test class
29
+ # for a set of mixins - give it the modules
30
+ # and it will include them all.
31
+ def test_class_for(*mods, &blk)
32
+ klass = Class.new
33
+ klass.class_eval { include(*mods) }
34
+ klass.class_eval(&blk) unless blk.blank?
35
+ return klass
36
+ end
37
+
38
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perennial
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.2
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Darcy Laycock
@@ -9,35 +9,56 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-09-25 00:00:00 +08:00
12
+ date: 2009-10-05 00:00:00 +08:00
13
13
  default_executable: perennial
14
- dependencies: []
15
-
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thoughtbot-shoulda
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: yard
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
16
35
  description: Perennial is a platform for building different applications in Ruby. It uses a controller-based approach with mixins to provide different functionality.
17
36
  email: sutto@sutto.net
18
37
  executables:
19
38
  - perennial
20
39
  extensions: []
21
40
 
22
- extra_rdoc_files: []
23
-
41
+ extra_rdoc_files:
42
+ - README.md
24
43
  files:
25
44
  - bin/perennial
45
+ - lib/perennial.rb
26
46
  - lib/perennial/application.rb
27
47
  - lib/perennial/argument_parser.rb
48
+ - lib/perennial/core_ext.rb
28
49
  - lib/perennial/core_ext/attribute_accessors.rb
29
50
  - lib/perennial/core_ext/blank.rb
30
51
  - lib/perennial/core_ext/hash_key_conversions.rb
31
52
  - lib/perennial/core_ext/inflections.rb
32
53
  - lib/perennial/core_ext/misc.rb
33
54
  - lib/perennial/core_ext/proxy.rb
34
- - lib/perennial/core_ext.rb
35
55
  - lib/perennial/daemon.rb
36
56
  - lib/perennial/delegateable.rb
37
57
  - lib/perennial/dispatchable.rb
38
58
  - lib/perennial/exceptions.rb
39
59
  - lib/perennial/generator.rb
40
60
  - lib/perennial/hookable.rb
61
+ - lib/perennial/jeweler_ext.rb
41
62
  - lib/perennial/loader.rb
42
63
  - lib/perennial/loggable.rb
43
64
  - lib/perennial/logger.rb
@@ -46,20 +67,20 @@ files:
46
67
  - lib/perennial/option_parser.rb
47
68
  - lib/perennial/reloading.rb
48
69
  - lib/perennial/settings.rb
49
- - lib/perennial.rb
50
70
  - templates/application.erb
51
71
  - templates/boot.erb
52
72
  - templates/rakefile.erb
53
73
  - templates/setup.erb
54
74
  - templates/test.erb
55
75
  - templates/test_helper.erb
56
- has_rdoc: false
76
+ - README.md
77
+ has_rdoc: true
57
78
  homepage: http://sutto.net/
58
79
  licenses: []
59
80
 
60
81
  post_install_message:
61
- rdoc_options: []
62
-
82
+ rdoc_options:
83
+ - --charset=UTF-8
63
84
  require_paths:
64
85
  - lib
65
86
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -77,9 +98,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
98
  requirements: []
78
99
 
79
100
  rubyforge_project:
80
- rubygems_version: 1.3.2
101
+ rubygems_version: 1.3.5
81
102
  signing_key:
82
103
  specification_version: 3
83
104
  summary: A simple (generally event-oriented) application library for Ruby
84
- test_files: []
85
-
105
+ test_files:
106
+ - test/delegateable_test.rb
107
+ - test/dispatchable_test.rb
108
+ - test/hookable_test.rb
109
+ - test/loader_test.rb
110
+ - test/loggable_test.rb
111
+ - test/logger_test.rb
112
+ - test/proxy_test.rb
113
+ - test/settings_test.rb
114
+ - test/test_helper.rb