contxtlservice 0.1.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/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:
|