contxtlservice 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: