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.
Files changed (2) hide show
  1. data/lib/contxtlservice.rb +171 -0
  2. 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: