methodmissing-scrooge 1.0.0
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.textile +262 -0
- data/lib/scrooge/core/string.rb +29 -0
- data/lib/scrooge/core/symbol.rb +21 -0
- data/lib/scrooge/core/thread.rb +26 -0
- data/lib/scrooge/framework/base.rb +304 -0
- data/lib/scrooge/framework/rails.rb +107 -0
- data/lib/scrooge/middleware/tracker.rb +47 -0
- data/lib/scrooge/orm/active_record.rb +143 -0
- data/lib/scrooge/orm/base.rb +102 -0
- data/lib/scrooge/profile.rb +204 -0
- data/lib/scrooge/storage/base.rb +47 -0
- data/lib/scrooge/storage/memory.rb +25 -0
- data/lib/scrooge/tracker/app.rb +101 -0
- data/lib/scrooge/tracker/base.rb +56 -0
- data/lib/scrooge/tracker/model.rb +90 -0
- data/lib/scrooge/tracker/resource.rb +201 -0
- data/lib/scrooge.rb +62 -0
- data/spec/fixtures/config/scrooge/scopes/1234567891/scope.yml +2 -0
- data/spec/fixtures/config/scrooge.yml +12 -0
- data/spec/helpers/framework/rails/cache.rb +25 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/units/scrooge/core/string_spec.rb +21 -0
- data/spec/units/scrooge/core/symbol_spec.rb +13 -0
- data/spec/units/scrooge/core/thread_spec.rb +15 -0
- data/spec/units/scrooge/framework/base_spec.rb +154 -0
- data/spec/units/scrooge/framework/rails_spec.rb +40 -0
- data/spec/units/scrooge/orm/base_spec.rb +61 -0
- data/spec/units/scrooge/profile_spec.rb +73 -0
- data/spec/units/scrooge/storage/base_spec.rb +35 -0
- data/spec/units/scrooge/storage/memory_spec.rb +20 -0
- data/spec/units/scrooge/tracker/app_spec.rb +62 -0
- data/spec/units/scrooge/tracker/base_spec.rb +21 -0
- data/spec/units/scrooge/tracker/model_spec.rb +50 -0
- data/spec/units/scrooge/tracker/resource_spec.rb +83 -0
- data/spec/units/scrooge_spec.rb +13 -0
- metadata +110 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
module Scrooge
|
2
|
+
module Framework
|
3
|
+
class Rails < Base
|
4
|
+
|
5
|
+
# Look for RAILS_ROOT and Rails.
|
6
|
+
|
7
|
+
signature do
|
8
|
+
defined?(RAILS_ROOT)
|
9
|
+
end
|
10
|
+
|
11
|
+
signature do
|
12
|
+
Object.const_defined?( "Rails" )
|
13
|
+
end
|
14
|
+
|
15
|
+
def environment
|
16
|
+
::RAILS_ENV
|
17
|
+
end
|
18
|
+
|
19
|
+
def root
|
20
|
+
::Rails.root
|
21
|
+
end
|
22
|
+
|
23
|
+
def tmp
|
24
|
+
@tmp ||= File.join( ::Rails.root, 'tmp' )
|
25
|
+
end
|
26
|
+
|
27
|
+
def config
|
28
|
+
@config ||= File.join( ::Rails.root, 'config' )
|
29
|
+
end
|
30
|
+
|
31
|
+
def logger
|
32
|
+
::Rails.logger
|
33
|
+
end
|
34
|
+
|
35
|
+
def resource( env, request = nil )
|
36
|
+
GUARD.synchronize do
|
37
|
+
# TODO: Wonky practice to piggy back on this current Edge / 2.3 hack
|
38
|
+
request = request || env['action_controller.rescue.request']
|
39
|
+
supplement_current_resource!( request )
|
40
|
+
Thread.scrooge_resource = Scrooge::Base.profile.tracker.resource_for( Thread.scrooge_resource )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def read_cache( key )
|
45
|
+
::Rails.cache.read( key )
|
46
|
+
end
|
47
|
+
|
48
|
+
def write_cache( key, value )
|
49
|
+
::Rails.cache.write( key, value )
|
50
|
+
end
|
51
|
+
|
52
|
+
def middleware
|
53
|
+
::Rails.configuration.middleware
|
54
|
+
end
|
55
|
+
|
56
|
+
# Push the Tracking middleware into the first slot.
|
57
|
+
#
|
58
|
+
def install_tracking_middleware
|
59
|
+
GUARD.synchronize do
|
60
|
+
ActionController::Dispatcher.to_prepare( :scrooge_install_tracking_middleware ) do
|
61
|
+
ApplicationController.prepend_around_filter Scrooge::Middleware::Tracker
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Install per Resource scoping middleware.
|
67
|
+
#
|
68
|
+
def install_scope_middleware( tracker )
|
69
|
+
GUARD.synchronize do
|
70
|
+
ActionController::Dispatcher.to_prepare( :scrooge_install_scope_middleware ) do
|
71
|
+
tracker.resources.each do |resource|
|
72
|
+
install_scope_middleware_for_resource!( resource )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def initialized( &block )
|
79
|
+
begin
|
80
|
+
::Rails.configuration.after_initialize( &block )
|
81
|
+
rescue NameError
|
82
|
+
# No cofig initialized - plugin installation etc.
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def controller( resource )
|
87
|
+
"#{resource.controller}_controller".classify.constantize
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def install_scope_middleware_for_resource!( resource ) #:nodoc:
|
93
|
+
resource.middleware.each do |resource_middleware|
|
94
|
+
controller( resource ).prepend_around_filter resource_middleware, :only => resource.action
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def supplement_current_resource!( request ) #:nodoc:
|
99
|
+
Thread.scrooge_resource.controller = request.path_parameters['controller']
|
100
|
+
Thread.scrooge_resource.action = request.path_parameters['action']
|
101
|
+
Thread.scrooge_resource.method = request.method
|
102
|
+
Thread.scrooge_resource.format = request.format.to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Scrooge
|
2
|
+
module Middleware
|
3
|
+
class Tracker < Scrooge::Base
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# Around Filter compatible implementation for Rails as Dispatcher is
|
8
|
+
# the root Rack application and as such don't provide access to the Rails
|
9
|
+
# Routing internals from other middleware.
|
10
|
+
#
|
11
|
+
def filter( controller, &block )
|
12
|
+
Scrooge::Base.profile.tracker.track( Thread.scrooge_resource ) do
|
13
|
+
begin
|
14
|
+
Scrooge::Base.profile.framework.resource( {}, controller.request )
|
15
|
+
Scrooge::Base.profile.log "Track for Resource #{Thread.scrooge_resource.inspect}"
|
16
|
+
block.call
|
17
|
+
ensure
|
18
|
+
Thread.reset_scrooge_resource!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(app, options = {})
|
26
|
+
@app = app
|
27
|
+
end
|
28
|
+
|
29
|
+
# Assign a default Resource Tracker instance to Thread.current[:scrooge_resource]
|
30
|
+
# and supplement it with request specific details ( format, action && controller )
|
31
|
+
# after yielding to the app.Flush Thread.current[:scrooge_resource] on completion.
|
32
|
+
#
|
33
|
+
def call(env)
|
34
|
+
Scrooge::Base.profile.tracker.track( Thread.scrooge_resource ) do
|
35
|
+
begin
|
36
|
+
result = @app.call(env)
|
37
|
+
Scrooge::Base.profile.framework.resource( env )
|
38
|
+
result
|
39
|
+
ensure
|
40
|
+
Thread.reset_scrooge_resource!
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Scrooge
|
2
|
+
module Orm
|
3
|
+
class ActiveRecord < Base
|
4
|
+
module ScroogeAttributes
|
5
|
+
|
6
|
+
module SingletonMethods
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
# Attach to generated attribute reader methods.
|
11
|
+
|
12
|
+
def define_read_method(symbol, attr_name, column)
|
13
|
+
register_with_scrooge!( attr_name, 'define read method' )
|
14
|
+
super(symbol, attr_name, column)
|
15
|
+
end
|
16
|
+
|
17
|
+
def define_read_method_for_time_zone_conversion(attr_name)
|
18
|
+
register_with_scrooge!( attr_name, 'define read method for time zone conversion' )
|
19
|
+
super(attr_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def define_read_method_for_serialized_attribute(attr_name)
|
23
|
+
register_with_scrooge!( attr_name, 'define read method for serialized attribute' )
|
24
|
+
super(attr_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def register_with_scrooge!( attr_name, caller ) #:nodoc:
|
30
|
+
if ::Scrooge::Base.profile.orm.track?
|
31
|
+
logger.info "[Scrooge] #{caller} #{attr_name.to_s}"
|
32
|
+
Thread.scrooge_resource << [self.base_class, attr_name]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
module InstanceMethods
|
39
|
+
|
40
|
+
# Attach to AR::Base#read_attribute.
|
41
|
+
#
|
42
|
+
def read_attribute(attr_name)
|
43
|
+
register_with_scrooge!( attr_name, 'read attribute' )
|
44
|
+
super( attr_name )
|
45
|
+
end
|
46
|
+
|
47
|
+
# Attach to AR::Base#read_attribute_before_typecast.
|
48
|
+
#
|
49
|
+
def read_attribute_before_type_cast(attr_name)
|
50
|
+
register_with_scrooge!( attr_name, 'read attribute before type cast' )
|
51
|
+
super(attr_name)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def register_with_scrooge!( attr_name, caller ) #:nodoc:
|
57
|
+
if ::Scrooge::Base.profile.orm.track?
|
58
|
+
logger.info "[Scrooge] #{caller} #{attr_name.to_s}"
|
59
|
+
Thread.scrooge_resource << [self.class.base_class, attr_name]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def missing_attribute(attr_name, stack) #:nodoc:
|
64
|
+
if Scrooge::Base.profile.raise_on_missing_attribute?
|
65
|
+
super(attr_name, stack)
|
66
|
+
else
|
67
|
+
logger.info "[Scrooge] missing attribute #{attr_name.to_s}"
|
68
|
+
reload( :select => '*' )
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
class << self
|
77
|
+
|
78
|
+
# Inject Scrooge ActiveRecord attribute tracking.
|
79
|
+
#
|
80
|
+
def install!
|
81
|
+
::ActiveRecord::Base.send( :extend, Scrooge::Orm::ActiveRecord::ScroogeAttributes::SingletonMethods )
|
82
|
+
::ActiveRecord::Base.send( :include, Scrooge::Orm::ActiveRecord::ScroogeAttributes::InstanceMethods )
|
83
|
+
end
|
84
|
+
|
85
|
+
# Determine if the ActiveRecord attribute tracker has already been installed.
|
86
|
+
#
|
87
|
+
def installed?
|
88
|
+
begin
|
89
|
+
::ActiveRecord::Base.included_modules.include?( Scrooge::Orm::ActiveRecord::ScroogeAttributes )
|
90
|
+
rescue => exception
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
# Generate scope helpers for a given resource.
|
97
|
+
#
|
98
|
+
def scope_to( resource )
|
99
|
+
resource.models.each do |model|
|
100
|
+
scope_resource_to_model( resource, model )
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Generate scope helpers for a given model and resource.
|
105
|
+
#
|
106
|
+
def scope_resource_to_model( resource, model )
|
107
|
+
method_name = resource_scope_method( resource )
|
108
|
+
klass = model.model.to_const!(false) if model.model.is_a?(String)
|
109
|
+
unless resource_scope_method?( resource, klass )
|
110
|
+
klass.instance_eval(<<-EOS, __FILE__, __LINE__)
|
111
|
+
def #{method_name}(&block)
|
112
|
+
with_scope( { :find => { :select => '#{model.to_sql}' } }) do
|
113
|
+
block.call
|
114
|
+
end
|
115
|
+
end
|
116
|
+
EOS
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns a lookup key from a given String or AR klass
|
121
|
+
#
|
122
|
+
def name( model )
|
123
|
+
model = model.to_const!(false) if model.is_a?(String)
|
124
|
+
model.respond_to?(:base_class) ? model.base_class.to_s : model.to_s
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns a table name from a given String or AR klass
|
128
|
+
#
|
129
|
+
def table_name( model )
|
130
|
+
model = model.to_const!(false) if model.is_a?(String)
|
131
|
+
model.table_name
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns a primary key from a given String or AR klass
|
135
|
+
#
|
136
|
+
def primary_key( model )
|
137
|
+
model = model.to_const!(false) if model.is_a?(String)
|
138
|
+
model.primary_key
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Scrooge
|
2
|
+
module Orm
|
3
|
+
|
4
|
+
autoload :ActiveRecord, 'scrooge/orm/active_record'
|
5
|
+
|
6
|
+
class Base < Scrooge::Base
|
7
|
+
|
8
|
+
# ORM agnostic base class.
|
9
|
+
|
10
|
+
class NotImplemented < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
# Instantiate and optionally install from a given ORM signature.
|
16
|
+
#
|
17
|
+
def instantiate( orm_signature )
|
18
|
+
orm_instance = "scrooge/orm/#{orm_signature.to_s}".to_const!
|
19
|
+
orm_instance.class.install! unless orm_instance.class.installed?
|
20
|
+
orm_instance
|
21
|
+
end
|
22
|
+
|
23
|
+
# Subclasses is required to implemented an installation hook.
|
24
|
+
#
|
25
|
+
def install!
|
26
|
+
raise NotImplemented
|
27
|
+
end
|
28
|
+
|
29
|
+
# Subclasses should be able to determine if it's already been injected into
|
30
|
+
# the underlying ORM package.
|
31
|
+
#
|
32
|
+
def installed?
|
33
|
+
raise NotImplemented
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generate scope helpers for a Resource and it's related Model trackers.
|
39
|
+
#
|
40
|
+
def scope_to( resource )
|
41
|
+
raise NotImplemented
|
42
|
+
end
|
43
|
+
|
44
|
+
# Generate scope helpers for a given Resource and Model tracker.
|
45
|
+
#
|
46
|
+
def scope_resource_to_model( resource, model )
|
47
|
+
raise NotImplemented
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a lookup key from a given String or class
|
51
|
+
#
|
52
|
+
def name( model )
|
53
|
+
raise NotImplemented
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns a table name from a given String or class
|
57
|
+
#
|
58
|
+
def table_name( model )
|
59
|
+
raise NotImplemented
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns a primary key from a given String or class
|
63
|
+
#
|
64
|
+
def primary_key( model )
|
65
|
+
raise NotImplemented
|
66
|
+
end
|
67
|
+
|
68
|
+
# Generates a sanitized method name for a given resource.
|
69
|
+
#
|
70
|
+
def resource_scope_method( resource )
|
71
|
+
"scope_to_#{resource.signature}".to_sym
|
72
|
+
end
|
73
|
+
|
74
|
+
# Determine if a scope method has already been generated for a given
|
75
|
+
# Resource and klass.
|
76
|
+
#
|
77
|
+
def resource_scope_method?( resource, klass )
|
78
|
+
klass.respond_to?( resource_scope_method( resource ) )
|
79
|
+
end
|
80
|
+
|
81
|
+
# Only track if the current profile is configured for tracking and a tracker
|
82
|
+
# resource is active, iow. we're in the scope of a request.
|
83
|
+
#
|
84
|
+
def track?
|
85
|
+
profile.track? && resource?
|
86
|
+
end
|
87
|
+
|
88
|
+
# Do we have access to Resource Tracker instance ?
|
89
|
+
#
|
90
|
+
def resource?
|
91
|
+
!resource().nil?
|
92
|
+
end
|
93
|
+
|
94
|
+
# Delegate to Thread.current
|
95
|
+
#
|
96
|
+
def resource
|
97
|
+
Thread.current[:scrooge_resource]
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Scrooge
|
4
|
+
class Profile
|
5
|
+
|
6
|
+
# A Profile for Scrooge that holds configuration, determines if we're tracking or
|
7
|
+
# scoping and provides access to a Tracker, ORM, Storage and Framework instance.
|
8
|
+
|
9
|
+
class Signature
|
10
|
+
end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
# Setup a new instance from the path to a YAML configuration file and a
|
15
|
+
# given environment.
|
16
|
+
#
|
17
|
+
def setup( path, environment )
|
18
|
+
begin
|
19
|
+
new( read_config( path, environment ) )
|
20
|
+
rescue Errno::ENOENT
|
21
|
+
puts "Scrooge Configuration file not available."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Pairs profile setup with the host framework.
|
26
|
+
#
|
27
|
+
def setup!
|
28
|
+
setup( framework.configuration_file, framework.environment )
|
29
|
+
end
|
30
|
+
|
31
|
+
# Yields an instance to a wrapper of the host framework.
|
32
|
+
#
|
33
|
+
def framework
|
34
|
+
@@framework ||= Scrooge::Framework::Base.instantiate
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def read_config( path, environment ) #:nodoc:
|
40
|
+
YAML.load( IO.read( path ) )[environment.to_s]
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_reader :options
|
46
|
+
|
47
|
+
def initialize( options = {} )
|
48
|
+
self.options = options
|
49
|
+
end
|
50
|
+
|
51
|
+
# options writer that attempts to reconfigure for any configuration changes.
|
52
|
+
#
|
53
|
+
def options=( options )
|
54
|
+
@options = options
|
55
|
+
configure!
|
56
|
+
end
|
57
|
+
|
58
|
+
# Delegates to the underlying ORM framework.
|
59
|
+
#
|
60
|
+
def orm
|
61
|
+
@orm_instance ||= Scrooge::Orm::Base.instantiate( @orm )
|
62
|
+
end
|
63
|
+
|
64
|
+
# Delegates to the current storage backend.
|
65
|
+
#
|
66
|
+
def storage
|
67
|
+
@storage_instance ||= Scrooge::Storage::Base.instantiate( @storage )
|
68
|
+
end
|
69
|
+
|
70
|
+
# Delegates to the current framework.
|
71
|
+
#
|
72
|
+
def framework
|
73
|
+
self.class.framework
|
74
|
+
end
|
75
|
+
|
76
|
+
# Log a message to the framework's logger.
|
77
|
+
#
|
78
|
+
def log( message )
|
79
|
+
framework.log( message ) rescue ''
|
80
|
+
end
|
81
|
+
|
82
|
+
# Delegates to the Application Tracker.
|
83
|
+
#
|
84
|
+
def tracker
|
85
|
+
@tracker_instance ||= Scrooge::Tracker::App.new
|
86
|
+
end
|
87
|
+
|
88
|
+
# Determine if this is a tracking or scope profile.
|
89
|
+
#
|
90
|
+
def track_or_scope!
|
91
|
+
if enabled?
|
92
|
+
track? ? track! : scope!
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Are we tracking ?
|
97
|
+
#
|
98
|
+
def track?
|
99
|
+
if enabled?
|
100
|
+
@track ||= (@scope || '').match( /\d{10}/ ).nil?
|
101
|
+
else
|
102
|
+
false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def track!
|
107
|
+
if track?
|
108
|
+
log "Tracking"
|
109
|
+
framework.install_tracking_middleware()
|
110
|
+
shutdown_hook!
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# The active scope signature
|
115
|
+
#
|
116
|
+
def scope_to
|
117
|
+
@scope
|
118
|
+
end
|
119
|
+
|
120
|
+
# Are we scoping ?
|
121
|
+
#
|
122
|
+
def scope?
|
123
|
+
if enabled?
|
124
|
+
!track?
|
125
|
+
else
|
126
|
+
false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Scope the tracker environment to a given scope signature.
|
131
|
+
#
|
132
|
+
def scope_to_signature!( scope_signature )
|
133
|
+
log "Scope to #{scope_signature}"
|
134
|
+
@tracker_instance = framework.from_scope!( scope_signature )
|
135
|
+
end
|
136
|
+
|
137
|
+
# Scope the tracker environment to a given scope signature and install
|
138
|
+
# scoping middleware.
|
139
|
+
#
|
140
|
+
def scope_to!
|
141
|
+
if scope?
|
142
|
+
scope_to_signature!( @scope )
|
143
|
+
framework.install_scope_middleware( tracker )
|
144
|
+
end
|
145
|
+
end
|
146
|
+
alias :scope! :scope_to!
|
147
|
+
|
148
|
+
# Should Scrooge inject itself ?
|
149
|
+
#
|
150
|
+
def enabled?
|
151
|
+
@enabled
|
152
|
+
end
|
153
|
+
|
154
|
+
# Should we raise on missing attributes ?
|
155
|
+
#
|
156
|
+
def raise_on_missing_attribute?
|
157
|
+
@on_missing_attribute == :raise
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def configure! #:nodoc:
|
163
|
+
@orm = configure_with( @options['orm'], [:active_record], :active_record )
|
164
|
+
@storage = configure_with( @options['storage'], [:memory], :memory )
|
165
|
+
@scope = configure_with( @options['scope'].to_s, framework.scopes, ENV['scope'] )
|
166
|
+
@enabled = configure_with( @options['enabled'], [true, false], false )
|
167
|
+
@on_missing_attribute = configure_with( @options['on_missing_attribute'], [:reload, :raise], :reload )
|
168
|
+
reset_backends!
|
169
|
+
memoize_backends!
|
170
|
+
end
|
171
|
+
|
172
|
+
def configure_with( given, valid, default ) #:nodoc:
|
173
|
+
if given
|
174
|
+
valid.include?( given ) ? given : default
|
175
|
+
else
|
176
|
+
default
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def reset_backends! #:nodoc:
|
181
|
+
@orm_instance = nil
|
182
|
+
@tracker_instance = nil
|
183
|
+
@storage_instance = nil
|
184
|
+
end
|
185
|
+
|
186
|
+
# Force constant lookups as autoload is not threadsafe.
|
187
|
+
#
|
188
|
+
def memoize_backends! #:nodoc:
|
189
|
+
framework()
|
190
|
+
orm()
|
191
|
+
storage()
|
192
|
+
tracker()
|
193
|
+
end
|
194
|
+
|
195
|
+
def shutdown_hook! #:nodoc:
|
196
|
+
# Registers an at_exit hook to persist the current application scope.
|
197
|
+
::Kernel.at_exit do
|
198
|
+
log "shutdown ..."
|
199
|
+
framework.scope! if tracker.any?
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Scrooge
|
2
|
+
module Storage
|
3
|
+
|
4
|
+
autoload :Buffer, 'scrooge/storage/buffer'
|
5
|
+
autoload :Memory, 'scrooge/storage/memory'
|
6
|
+
|
7
|
+
class Base < Scrooge::Base
|
8
|
+
class NotImplemented < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
# A Single Mutex for all Storage subclasses as their's only one storage instance.
|
12
|
+
#
|
13
|
+
GUARD = Mutex.new
|
14
|
+
|
15
|
+
NAMESPACE = 'scrooge_storage'.freeze
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
# Yields a storage instance from a given signature.
|
20
|
+
#
|
21
|
+
def instantiate( storage_signature )
|
22
|
+
"scrooge/storage/#{storage_signature.to_s}".to_const!
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
# Retrieve a given tracker from storage.
|
28
|
+
#
|
29
|
+
def read( tracker )
|
30
|
+
raise NotImplemented
|
31
|
+
end
|
32
|
+
|
33
|
+
# Persist a given tracker to storage.
|
34
|
+
#
|
35
|
+
def write( tracker, buffered = true )
|
36
|
+
raise NotImplemented
|
37
|
+
end
|
38
|
+
|
39
|
+
# Namespace lookup keys.
|
40
|
+
#
|
41
|
+
def expand_key( key )
|
42
|
+
"#{NAMESPACE}/#{key}"
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Scrooge
|
2
|
+
module Storage
|
3
|
+
class Memory < Base
|
4
|
+
|
5
|
+
attr_reader :storage
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@storage = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def read( tracker )
|
12
|
+
GUARD.synchronize do
|
13
|
+
@storage[tracker.signature]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def write( tracker, buffered = true )
|
18
|
+
GUARD.synchronize do
|
19
|
+
@storage[tracker.signature] = tracker
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|