contxtlservice 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/contxtlservice.rb +171 -0
- metadata +59 -0
@@ -0,0 +1,171 @@
|
|
1
|
+
# The ContextualService library makes it easy to manage and set services to a
|
2
|
+
# single, global resource such as a database or file system. By defining a
|
3
|
+
# service to be a child of ContextualService::Service, you
|
4
|
+
# * lock off access to instantiation, so all access have to go through the
|
5
|
+
# service's get method,
|
6
|
+
# * and provide a simple way to set the instance, most likely for testing.
|
7
|
+
#
|
8
|
+
# For example, Lafcadio ( http://lafcadio.rubyforge.org ) uses
|
9
|
+
# ContextualService to manage access to the ObjectStore, which controls access
|
10
|
+
# to a database.
|
11
|
+
#
|
12
|
+
# class Lafcadio::ObjectStore < ContextualService::Service
|
13
|
+
# def initialize
|
14
|
+
# ...
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# ObjectStore.new will raise an error. To get an instance of ObjectStore, you
|
19
|
+
# call ObjectStore.get_object_store. If no instance has been previously set,
|
20
|
+
# this will instantiate the ObjectStore and save the instance for future
|
21
|
+
# accesses. If a mock instance has been previously set with set_object_store,
|
22
|
+
# it will simply return that mock instance.
|
23
|
+
#
|
24
|
+
# Since access to the global service is attached to the class, there's no need
|
25
|
+
# to pass in a mock service as an argument to any method that needs it. This
|
26
|
+
# can simplify testing considerably if you've got highly decomposed code that
|
27
|
+
# needs to access a global service from many places. Also, since the Context
|
28
|
+
# object is hidden, clients of the service don't have to be conscious of it.
|
29
|
+
#
|
30
|
+
# def some_method
|
31
|
+
# os = Lafcadio::ObjectStore.get_object_store
|
32
|
+
# # do something with the object store here
|
33
|
+
# some_other_method
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# def some_other_method
|
37
|
+
# os = Lafcadio::ObjectStore.get_object_store
|
38
|
+
# # do something with the object store here, too
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# class TestSomeMethod < Test::Unit::TestCase
|
42
|
+
# def test1
|
43
|
+
# mock_object_store = Lafcadio::MockObjectStore.new
|
44
|
+
# Lafcadio::ObjectStore.set_object_store mock_object_store
|
45
|
+
# some_method
|
46
|
+
# # assert something happened
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# The project page can be found at
|
51
|
+
# http://rubyforge.org/projects/contxtlservice .
|
52
|
+
|
53
|
+
require 'englishext'
|
54
|
+
require 'extensions/all'
|
55
|
+
require 'singleton'
|
56
|
+
|
57
|
+
module ContextualService
|
58
|
+
Version = '0.1.0'
|
59
|
+
|
60
|
+
# Context controls the various Service instances. It's a Singleton, so if you
|
61
|
+
# want to use Context, you'll have to call Context.instance instead of
|
62
|
+
# Context.new.
|
63
|
+
class Context
|
64
|
+
include Singleton
|
65
|
+
|
66
|
+
def initialize #:nodoc:
|
67
|
+
flush
|
68
|
+
@init_procs = {}
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_instance( service_class, *init_args ) #:nodoc:
|
72
|
+
if ( proc = @init_procs[service_class] )
|
73
|
+
proc.call( *init_args )
|
74
|
+
else
|
75
|
+
service_class.new( *init_args )
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Flushes all cached Services.
|
80
|
+
def flush
|
81
|
+
@resources_by_class = Hash.new { |hash, key| hash[key] = {} }
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_resource( service_class, *init_args ) #:nodoc:
|
85
|
+
resource = @resources_by_class[service_class][init_args]
|
86
|
+
unless resource
|
87
|
+
resource = create_instance( service_class, *init_args )
|
88
|
+
set_resource( service_class, resource, *init_args )
|
89
|
+
end
|
90
|
+
resource
|
91
|
+
end
|
92
|
+
|
93
|
+
def set_init_proc( service_class, proc ) #:nodoc:
|
94
|
+
@init_procs[service_class] = proc
|
95
|
+
end
|
96
|
+
|
97
|
+
def set_resource( service_class, resource, *init_args ) #:nodoc:
|
98
|
+
@resources_by_class[service_class][init_args] = resource
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# A Service is the point of access to the global service itself. Using
|
103
|
+
# ContextualService mostly means creating subclasses of Service and then
|
104
|
+
# adding your domain-specific functionality to that subclass.
|
105
|
+
#
|
106
|
+
# To get a Service, call its get_< service_name > method:
|
107
|
+
# object_store = ObjectStore.get_object_store
|
108
|
+
#
|
109
|
+
# To set a Service, call its set_< service_name > method:
|
110
|
+
# ObjectStore.set_object_store MockObjectStore.new
|
111
|
+
class Service
|
112
|
+
# Flushes the currently saved instance of the Service in the Context; the
|
113
|
+
# next time Service.get_< service_name > is called, Context will
|
114
|
+
# re-intantiate the Service from scratch.
|
115
|
+
def self.flush; Context.instance.set_resource( self, nil ); end
|
116
|
+
|
117
|
+
def self.method_missing( symbol, *args ) #:nodoc:
|
118
|
+
method_name = symbol.id2name
|
119
|
+
target = nil
|
120
|
+
if method_name =~ /^get_(.*)/
|
121
|
+
target = :get_resource if $1.underscore_to_camel_case == basename
|
122
|
+
elsif method_name =~ /^set_(.*)/
|
123
|
+
target = :set_resource if $1.underscore_to_camel_case == basename
|
124
|
+
end
|
125
|
+
if target
|
126
|
+
Context.instance.send( target, self, *args )
|
127
|
+
else
|
128
|
+
super
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Sets what happens when Context is asked for a Service that hasn't yet been set or saved. By default, Context simply creates that Service and passes it on, but you can override that with a proc of some kind.
|
133
|
+
#
|
134
|
+
# ObjectStore.set_init_proc { MockObjectStore.new }
|
135
|
+
#
|
136
|
+
# Most of the time, it will just be simpler to use
|
137
|
+
# Service.set_< service_name > instead.
|
138
|
+
def self.set_init_proc
|
139
|
+
proc = proc { yield }
|
140
|
+
Context.instance.set_init_proc( self, proc )
|
141
|
+
end
|
142
|
+
|
143
|
+
# A Service can only be initialized through the Context instance: This
|
144
|
+
# behavior is enforced during initialization. So if you child class has its
|
145
|
+
# own initialize method, you should make sure to call super() to keep this
|
146
|
+
# restriction.
|
147
|
+
def initialize
|
148
|
+
regexp = %r{[^_]contxtlservice\.rb.*create_instance}
|
149
|
+
unless caller.any? { |line| line =~ regexp }
|
150
|
+
raise ArgumentError,
|
151
|
+
"#{ self.class.name.to_s } should be instantiated by calling " +
|
152
|
+
self.class.name.to_s + ".get_" + self.class.name.camel_case_to_underscore,
|
153
|
+
caller
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class String
|
160
|
+
unless method_defined?( :camel_case_to_underscore )
|
161
|
+
def camel_case_to_underscore
|
162
|
+
( gsub( /(.)([A-Z])/ ) { $1 + '_' + $2.downcase } ).downcase
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
unless method_defined?( :underscore_to_camel_case )
|
167
|
+
def underscore_to_camel_case
|
168
|
+
capitalize.gsub( /_([a-zA-Z0-9]+)/ ) { |s| s[1,s.size - 1].capitalize }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.6
|
3
|
+
specification_version: 1
|
4
|
+
name: contxtlservice
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.1
|
7
|
+
date: 2005-07-04
|
8
|
+
summary: "The ContextualService library makes it easy to manage and set services to a
|
9
|
+
single, global resource such as a database or file system."
|
10
|
+
require_paths:
|
11
|
+
- lib
|
12
|
+
email: sera@fhwang.net
|
13
|
+
homepage: http://contxtlservice.rubyforge.org/
|
14
|
+
rubyforge_project:
|
15
|
+
description: "The ContextualService library makes it easy to manage and set services to a
|
16
|
+
single, global resource such as a database or file system."
|
17
|
+
autorequire: contxtlservice
|
18
|
+
default_executable:
|
19
|
+
bindir: bin
|
20
|
+
has_rdoc: false
|
21
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
22
|
+
requirements:
|
23
|
+
-
|
24
|
+
- ">"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.0
|
27
|
+
version:
|
28
|
+
platform: ruby
|
29
|
+
authors:
|
30
|
+
- Francis Hwang
|
31
|
+
files:
|
32
|
+
- lib/contxtlservice.rb
|
33
|
+
test_files: []
|
34
|
+
rdoc_options: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
requirements: []
|
39
|
+
dependencies:
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: englishext
|
42
|
+
version_requirement:
|
43
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
44
|
+
requirements:
|
45
|
+
-
|
46
|
+
- ">"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 0.0.0
|
49
|
+
version:
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: extensions
|
52
|
+
version_requirement:
|
53
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
54
|
+
requirements:
|
55
|
+
-
|
56
|
+
- ">"
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: 0.0.0
|
59
|
+
version:
|