Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Pre-github

  • Loading branch information...
commit 951c1eb51c100cded1038ad4d2ba9dbc18e44aa6 0 parents
@methodmissing authored
Showing with 1,721 additions and 0 deletions.
  1. 0  README
  2. +19 −0 Rakefile
  3. +1 −0  init.rb
  4. +37 −0 lib/scrooge.rb
  5. +16 −0 lib/scrooge/core/string.rb
  6. +15 −0 lib/scrooge/core/symbol.rb
  7. +148 −0 lib/scrooge/framework/base.rb
  8. +72 −0 lib/scrooge/framework/rails.rb
  9. +23 −0 lib/scrooge/middleware/tracker.rb
  10. +63 −0 lib/scrooge/orm/active_record.rb
  11. +64 −0 lib/scrooge/orm/base.rb
  12. +89 −0 lib/scrooge/profile.rb
  13. +41 −0 lib/scrooge/storage/base.rb
  14. +77 −0 lib/scrooge/storage/buffer.rb
  15. +19 −0 lib/scrooge/storage/cache.rb
  16. +44 −0 lib/scrooge/storage/file_system.rb
  17. +21 −0 lib/scrooge/storage/memory.rb
  18. +62 −0 lib/scrooge/tracker/app.rb
  19. +48 −0 lib/scrooge/tracker/base.rb
  20. +43 −0 lib/scrooge/tracker/model.rb
  21. +102 −0 lib/scrooge/tracker/resource.rb
  22. +1 −0  rails/init.rb
  23. +12 −0 spec/fixtures/config/scrooge.yml
  24. +25 −0 spec/helpers/framework/rails/cache.rb
  25. +52 −0 spec/spec_helper.rb
  26. +13 −0 spec/units/scrooge/core/string_spec.rb
  27. +13 −0 spec/units/scrooge/core/symbol_spec.rb
  28. +99 −0 spec/units/scrooge/framework/base_spec.rb
  29. +41 −0 spec/units/scrooge/framework/rails_spec.rb
  30. +57 −0 spec/units/scrooge/orm/base_spec.rb
  31. +65 −0 spec/units/scrooge/profile_spec.rb
  32. +67 −0 spec/units/scrooge/storage/base_spec.rb
  33. +24 −0 spec/units/scrooge/storage/cache_spec.rb
  34. +36 −0 spec/units/scrooge/storage/file_system_spec.rb
  35. +20 −0 spec/units/scrooge/storage/memory_spec.rb
  36. +50 −0 spec/units/scrooge/tracker/app_spec.rb
  37. +21 −0 spec/units/scrooge/tracker/base_spec.rb
  38. +39 −0 spec/units/scrooge/tracker/model_spec.rb
  39. +69 −0 spec/units/scrooge/tracker/resource_spec.rb
  40. +13 −0 spec/units/scrooge_spec.rb
0  README
No changes.
19 Rakefile
@@ -0,0 +1,19 @@
+begin
+ require 'spec'
+rescue LoadError
+ require 'rubygems'
+ require 'spec'
+end
+
+require 'spec/rake/spectask'
+
+$LOAD_PATH.unshift File.dirname(__FILE__) + '/../'
+$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib'
+
+require 'scrooge'
+
+desc "Run the specs under spec"
+Spec::Rake::SpecTask.new do |t|
+ t.spec_files = FileList['spec/**/*_spec.rb']
+ t.spec_opts << "-c"
+end
1  init.rb
@@ -0,0 +1 @@
+require 'scrooge'
37 lib/scrooge.rb
@@ -0,0 +1,37 @@
+$:.unshift(File.dirname(__FILE__))
+
+require 'scrooge/core/string'
+require 'scrooge/core/symbol'
+
+module Scrooge
+ class Base
+
+ class << self
+
+ def profile
+ @@profile ||= Scrooge::Profile.new
+ end
+
+ def profile=( profile )
+ @@profile = profile
+ end
+
+ end
+
+ def profile
+ self.class.profile
+ end
+
+ end
+
+ module Middleware
+ autoload :Tracker, 'scrooge/middleware/tracker'
+ end
+
+end
+
+require 'scrooge/profile'
+require 'scrooge/storage/base'
+require 'scrooge/orm/base'
+require 'scrooge/framework/base'
+require 'scrooge/tracker/base'
16 lib/scrooge/core/string.rb
@@ -0,0 +1,16 @@
+module Scrooge
+ module Core
+ module String
+
+ # Thx ActiveSupport
+ def to_const
+ self.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
+ end
+
+ end
+ end
+end
+
+class String
+ include Scrooge::Core::String
+end
15 lib/scrooge/core/symbol.rb
@@ -0,0 +1,15 @@
+module Scrooge
+ module Core
+ module Symbol
+
+ def to_const
+ to_s.to_const
+ end
+
+ end
+ end
+end
+
+class Symbol
+ include Scrooge::Core::Symbol
+end
148 lib/scrooge/framework/base.rb
@@ -0,0 +1,148 @@
+module Scrooge
+ module Framework
+
+ # Scrooge is framework agnostic and attempts to abstract the following :
+ #
+ # * current environment
+ # * app root dir
+ # * app tmp dir
+ # * app config dir
+ # * logging
+ # * resource endpoints
+ # * caching
+ # * injecting Rack MiddleWare
+ #
+ # Framework Signatures
+ #
+ # Scrooge will attempt to determine the current active framework it's deployed with
+ # through various framework specific hooks.
+ #
+ # module Scrooge
+ # module Framework
+ # module YetAnother < Base
+ # ...
+ # signature do
+ # Object.const_defined?( "UnlikeAnyOther" )
+ # end
+ # ...
+ # end
+ # end
+ # end
+
+ autoload :Rails, 'scrooge/framework/rails'
+
+ class Base < Scrooge::Base
+ class NotImplemented < StandardError
+ end
+
+ class NoSupportedFrameworks < StandardError
+ end
+
+ class << self
+
+ @@signatures = {}
+ @@signatures[self.name] = Hash.new( [] )
+ @@frameworks = []
+
+ def signature( &block )
+ @@signatures[self.name] = signatures << block
+ end
+
+ def signatures
+ @@signatures[self.name] || []
+ end
+
+ def frameworks
+ @@frameworks
+ end
+
+ def which_framework?
+ iterate_frameworks() || raise( NoSupportedFrameworks )
+ end
+
+ def instantiate
+ Object.module_eval("::#{which_framework?()}", __FILE__, __LINE__).new
+ end
+
+ private
+
+ def inherited( subclass ) #:nodoc:
+ @@frameworks << subclass
+ end
+
+ def iterate_frameworks #:nodoc:
+ frameworks.detect do |framework|
+ framework.signatures.all?{|sig| sig.call }
+ end
+ end
+
+ end
+
+ def environment
+ raise NotImplemented
+ end
+
+ def root
+ raise NotImplemented
+ end
+
+ def tmp
+ raise NotImplemented
+ end
+
+ def config
+ raise NotImplemented
+ end
+
+ def logger
+ raise NotImplemented
+ end
+
+ def resource( app )
+ raise NotImplemented
+ end
+
+ def write_cache( key, value )
+ raise NotImplemented
+ end
+
+ def read_cache( key )
+ raise NotImplemented
+ end
+
+ def middleware( &block )
+ raise NotImplemented
+ end
+
+ def install_scope_middleware( tracker )
+ raise NotImplemented
+ end
+
+ def install_tracking_middleware
+ raise NotImplemented
+ end
+
+ def scopes
+ ensure_scopes_path do
+ Dir.entries( scopes_path ).grep(/\d{10}/)
+ end
+ end
+
+ def scopes_path
+ @profiles_path ||= File.join( config, 'scrooge', 'scopes' )
+ end
+
+ def scope_path( scope )
+ File.join( scopes_path, scope.to_s )
+ end
+
+ private
+
+ def ensure_scopes_path #:nodoc:
+ FileUtils.makedirs( scopes_path ) unless File.exist?( scopes_path )
+ yield if block_given?
+ end
+
+ end
+ end
+end
72 lib/scrooge/framework/rails.rb
@@ -0,0 +1,72 @@
+module Scrooge
+ module Framework
+ class Rails < Base
+
+ signature do
+ defined?(RAILS_ROOT)
+ end
+
+ signature do
+ Object.const_defined?( "ActiveSupport" )
+ end
+
+ signature do
+ Object.const_defined?( "ActionController" )
+ end
+
+ def environment
+ ::Rails.env
+ end
+
+ def root
+ ::Rails.root
+ end
+
+ def tmp
+ File.join( ::Rails.root, 'tmp' )
+ end
+
+ def config
+ File.join( ::Rails.root, 'config' )
+ end
+
+ def logger
+ ::Rails.logger
+ end
+
+ def resource( app )
+ Scrooge::Tracker::Resource.new do |resource|
+ resource.controller = app.request.path_parameters['controller']
+ resource.action = app.request.path_parameters['action']
+ resource.method = app.request.method
+ resource.format = app.request.format
+ end
+ end
+
+ def read_cache( key )
+ ::Rails.cache.read( key )
+ end
+
+ def write_cache( key, value )
+ ::Rails.cache.write( key, value )
+ end
+
+ def middleware( &block )
+ ::ActionController::Dispatcher.middleware.instance_eval do
+ block.call
+ end
+ end
+
+ def install_scope_middleware( tracker )
+ tracker.resources.each do |resource|
+ tracker.middleware.each do |resource_middleware|
+ middleware do
+ use resource_middleware
+ end
+ end
+ end
+ end
+
+ end
+ end
+end
23 lib/scrooge/middleware/tracker.rb
@@ -0,0 +1,23 @@
+module Scrooge
+ module Middleware
+ class Tracker < Scrooge::Base
+
+ def initialize(app, options = {})
+ @app = app
+ end
+
+ def call(env)
+ Scrooge::Profile.tracker.track( resource ) do
+ @app.call(env)
+ end
+ end
+
+ private
+
+ def resource
+ Scrooge::Profile.framework.resource( @app )
+ end
+
+ end
+ end
+end
63 lib/scrooge/orm/active_record.rb
@@ -0,0 +1,63 @@
+module Scrooge
+ module Orm
+ class ActiveRecord < Base
+ module ScroogeAttributes
+
+ def self.include( base )
+ base.alias :original_read_attribute, :read_attribute
+ end
+
+ def read_attribute(attr_name)
+ if ::Scrooge::Profile.orm.track?
+ ::Scrooge::Profile.orm.resource << [self.class.base_class, attr_name]
+ end
+ original_read_attribute( attr_name )
+ end
+ end
+
+ class << self
+
+ def install!
+ include Scrooge::Orm::ActiveRecord::ScroogeAttributes
+ end
+
+ def installed?
+ include_modules.include?( Scrooge::Orm::ActiveRecord::ScroogeAttributes )
+ end
+
+ end
+
+ def scope_to( resource )
+ resource.models.each do |model|
+ scope_resource_to_model( resource, model )
+ end
+ end
+
+ def scope_resource_to_model( resource, model )
+ method_name = resource_scope_method( resource )
+ model.instance_eval do
+ (class << self; self end).send( :define_method, method_name ) do
+ model.model.with_scope( to_scope( model ) ) do
+ yield
+ end
+ end
+ end unless resource_scope_method?( resource )
+ end
+
+ def name( model )
+ model.base_name.to_s
+ end
+
+ def table_name( model )
+ model.table_name
+ end
+
+ private
+
+ def to_scope( model ) #:nodoc:
+ { :find => { :select => model.to_sql } }
+ end
+
+ end
+ end
+end
64 lib/scrooge/orm/base.rb
@@ -0,0 +1,64 @@
+module Scrooge
+ module Orm
+
+ autoload :ActiveRecord, 'scrooge/orm/active_record'
+
+ class Base < Scrooge::Base
+ class NotImplemented < StandardError
+ end
+
+ class << self
+
+ def instantiate( orm_signature )
+ Object.module_eval("::Scrooge::Orm::#{orm_signature.to_const}", __FILE__, __LINE__).new
+ end
+
+ def install!
+ raise NotImplemented
+ end
+
+ def installed?
+ raise NotImplemented
+ end
+
+ end
+
+ def scope_to( resource )
+ raise NotImplemented
+ end
+
+ def scope_resource_to_model( resource, model )
+ raise NotImplemented
+ end
+
+ def name( model )
+ raise NotImplemented
+ end
+
+ def table_name( model )
+ raise NotImplemented
+ end
+
+ def resource_scope_method( resource )
+ "scope_to_#{resource.signature}".to_sym
+ end
+
+ def resource_scope_method?( resource )
+ respond_to?( resource_scope_method( resource ) )
+ end
+
+ def track?
+ profile.track? && resource?
+ end
+
+ def resource?
+ !resource().nil?
+ end
+
+ def resource
+ Thread.current[:scrooge_resource]
+ end
+
+ end
+ end
+end
89 lib/scrooge/profile.rb
@@ -0,0 +1,89 @@
+require 'yaml'
+
+module Scrooge
+ class Profile
+
+ class Signature
+ end
+
+ class << self
+
+ def setup( path, environment )
+ new( YAML.load( IO.read( path ) )[environment.to_s] )
+ end
+
+ def framework
+ @@framework ||= Scrooge::Framework::Base.instantiate
+ end
+
+ end
+
+ attr_reader :options
+
+ def initialize( options = {} )
+ self.options = options
+ end
+
+ def options=( options )
+ @options = options
+ configure!
+ end
+
+ def warmup?
+ warmup_threshold != 0
+ end
+ alias :warmed_up? :warmup?
+
+ def buffer?
+ buffer_threshold != 0
+ end
+ alias :buffered? :buffer?
+
+ def buffer_threshold
+ @buffer_threshold.to_i
+ end
+
+ def warmup_threshold
+ @warmup_threshold.to_i
+ end
+
+ def orm
+ @orm_instance ||= Scrooge::Orm::Base.instantiate( @orm )
+ end
+
+ def storage
+ @storage_instance ||= Scrooge::Storage::Base.instantiate( @storage )
+ end
+
+ def framework
+ self.class.framework
+ end
+
+ def tracker
+ @tracker_instance ||= Scrooge::Tracker::App.new
+ end
+
+ def track?
+ @scope.nil?
+ end
+
+ def scope_to
+ @scope
+ end
+
+ def scope?
+ !track?
+ end
+
+ private
+
+ def configure! #:nodoc:
+ @orm = @options['orm'] || :active_record
+ @storage = @options['storage'] || :memory
+ @buffer_threshold = @options['buffer_threshold'] || 0
+ @warmup_threshold = @options['warmup_threshold'] || 0
+ @scope = @options['scope'] || nil
+ end
+
+ end
+end
41 lib/scrooge/storage/base.rb
@@ -0,0 +1,41 @@
+module Scrooge
+ module Storage
+
+ autoload :Buffer, 'scrooge/storage/buffer'
+ autoload :FileSystem, 'scrooge/storage/file_system'
+ autoload :Memory, 'scrooge/storage/memory'
+ autoload :Cache, 'scrooge/storage/cache'
+
+ class Base < Scrooge::Base
+ class NotImplemented < StandardError
+ end
+
+ NAMESPACE = 'scrooge_storage'.freeze
+
+ class << self
+
+ def instantiate( storage_signature )
+ Object.module_eval("::Scrooge::Storage::#{storage_signature.to_const}", __FILE__, __LINE__).new
+ end
+
+ end
+
+ def initialize
+ extend Buffer
+ end
+
+ def read( tracker )
+ raise NotImplemented
+ end
+
+ def write( tracker, buffered = true )
+ raise NotImplemented
+ end
+
+ def expand_key( key )
+ "#{NAMESPACE}/#{key}"
+ end
+
+ end
+ end
+end
77 lib/scrooge/storage/buffer.rb
@@ -0,0 +1,77 @@
+module Scrooge
+ module Storage
+ module Buffer
+
+ attr_accessor :storage_buffer,
+ :buffered_at
+
+ def storage_buffer
+ @storage_buffer ||= {}
+ end
+
+ def buffered_at
+ @buffered_at ||= Time.now.to_i
+ end
+
+ def read( tracker )
+ with_read_buffer( tracker ) do
+ super( tracker )
+ end
+ end
+
+ def write( tracker, buffered = true )
+ if buffered
+ with_write_buffer( tracker ) do
+ super( tracker, buffered )
+ end
+ else
+ super( tracker, buffered )
+ end
+ end
+
+ def buffer?
+ profile.buffer?
+ end
+
+ def flush_buffer?
+ if buffer?
+ ( buffered_at + profile.buffer_threshold ) < Time.now.to_i
+ else
+ false
+ end
+ end
+
+ def buffer( tracker )
+ storage_buffer[tracker.signature] = tracker
+ end
+
+ def flush!
+ while( !storage_buffer.empty? ) do
+ write( storage_buffer.shift.last, false )
+ end
+ buffered_at = Time.now.to_i
+ end
+
+ private
+
+ def with_read_buffer( tracker ) #:nodoc:
+ if flush_buffer?
+ flush!
+ storage_buffer[tracker.signature]
+ else
+ yield
+ end
+ end
+
+ def with_write_buffer( tracker ) #:nodoc:
+ if flush_buffer?
+ flush!
+ buffer( tracker )
+ else
+ yield
+ end
+ end
+
+ end
+ end
+end
19 lib/scrooge/storage/cache.rb
@@ -0,0 +1,19 @@
+module Scrooge
+ module Storage
+ class Cache < Base
+
+ def initialize
+ extend Buffer
+ end
+
+ def read( tracker )
+ profile.framework.read_cache( expand_key( tracker ) )
+ end
+
+ def write( tracker, buffered = true )
+ profile.framework.write_cache( expand_key( tracker ), tracker )
+ end
+
+ end
+ end
+end
44 lib/scrooge/storage/file_system.rb
@@ -0,0 +1,44 @@
+module Scrooge
+ module Storage
+ class FileSystem < Base
+
+ def initialize
+ extend Buffer
+ end
+
+ def read( tracker )
+ begin
+ File.open( tracker_file( tracker ), 'rb') {|t| Marshal.load(t) }
+ rescue => exception
+ profile.framework.logger.error "Scrooge: Could not read storage entry #{tracker.key} (#{exception.to_s})"
+ end
+ end
+
+ def write( tracker, buffered = true )
+ begin
+ ensure_tracker_path( tracker ) do
+ File.open( tracker_file( tracker ), 'w') {|t| Marshal.dump( tracker, t ) }
+ end
+ rescue => exception
+ profile.framework.logger.error "Scrooge: Could not write storage entry #{tracker.key} (#{exception.to_s})"
+ end
+ end
+
+ def tracker_file( tracker )
+ File.join( tracker_path( tracker ), 'scrooge' )
+ end
+
+ def tracker_path( tracker )
+ File.join( profile.framework.tmp, tracker.signature )
+ end
+
+ private
+
+ def ensure_tracker_path( tracker ) #:nodoc:
+ FileUtils.makedirs( tracker_path( tracker ) ) unless File.exist?( tracker_path( tracker ) )
+ yield if block_given?
+ end
+
+ end
+ end
+end
21 lib/scrooge/storage/memory.rb
@@ -0,0 +1,21 @@
+module Scrooge
+ module Storage
+ class Memory < Base
+
+ attr_reader :storage
+
+ def initialize
+ @storage = {}
+ end
+
+ def read( tracker )
+ @storage[tracker.signature]
+ end
+
+ def write( tracker, buffered = true )
+ @storage[tracker.signature] = tracker
+ end
+
+ end
+ end
+end
62 lib/scrooge/tracker/app.rb
@@ -0,0 +1,62 @@
+module Scrooge
+ module Tracker
+ class App < Scrooge::Tracker::Base
+
+ attr_accessor :resources
+
+ def initialize
+ super()
+ @resources = Set.new
+ end
+
+ def <<( resource )
+ @resources << resource
+ end
+
+ def marshal_dump
+ { environment() => dumped_resources() }
+ end
+
+ def marshal_load( data )
+ @resources = Set.new( restored_resources( data ) )
+ self
+ end
+
+ def track( resource )
+ if profile.track? && resource.trackable?
+ with_tracking( resource ) do
+ yield
+ end
+ else
+ yield
+ end
+ end
+
+ private
+
+ def with_tracking( resource )
+ Thread.current[:scrooge_resource] = resource
+ yield
+ ensure
+ Thread.current[:scrooge_resource] = nil
+ end
+
+ def environment #:nodoc:
+ profile.framework.environment
+ end
+
+ def restored_resources( data ) #:nodoc:
+ data[environment()].map do |resource|
+ Resource.new.marshal_load( resource )
+ end
+ end
+
+ def dumped_resources #:nodoc:
+ @resources.to_a.map do |resource|
+ resource.marshal_dump
+ end
+ end
+
+ end
+ end
+end
48 lib/scrooge/tracker/base.rb
@@ -0,0 +1,48 @@
+require 'set'
+
+module Scrooge
+ module Tracker
+
+ autoload :App, 'scrooge/tracker/app'
+ autoload :Resource, 'scrooge/tracker/resource'
+ autoload :Model, 'scrooge/tracker/model'
+
+ class Base < Scrooge::Base
+ include Comparable
+
+ class NotImplemented < StandardError
+ end
+
+ attr_accessor :counter
+
+ def initialize
+ @counter = 0
+ end
+
+ def to_i
+ @counter
+ end
+
+ def marshal_dump
+ raise NotImplemented
+ end
+
+ def marshal_load( data )
+ raise NotImplemented
+ end
+
+ def ==( tracker )
+ compare_to( tracker )
+ end
+ alias :eql? :==
+ alias :<=> :==
+
+ private
+
+ def compare_to( tracker ) #:nodoc:
+ marshal_dump == tracker.marshal_dump
+ end
+
+ end
+ end
+end
43 lib/scrooge/tracker/model.rb
@@ -0,0 +1,43 @@
+module Scrooge
+ module Tracker
+ class Model < Base
+
+ attr_accessor :model,
+ :attributes
+
+ def initialize( model )
+ super()
+ @model = model
+ @attributes = Set.new
+ end
+
+ def <<( attribute )
+ Array( attribute ).each do |attr|
+ attributes << attr
+ end
+ end
+
+ def marshal_dump
+ { name() => @attributes.to_a }
+ end
+
+ def marshal_load( data )
+ @attributes = Set.new( data[name()] )
+ self
+ end
+
+ def name
+ @name ||= profile.orm.name( @model )
+ end
+
+ def table_name
+ @table_name ||= profile.orm.table_name( @model )
+ end
+
+ def to_sql
+ @attributes.map{|a| "#{table_name}.#{a.to_s}" }.join(', ')
+ end
+
+ end
+ end
+end
102 lib/scrooge/tracker/resource.rb
@@ -0,0 +1,102 @@
+module Scrooge
+ module Tracker
+ class Resource < Base
+
+ GET = /get/i
+
+ attr_accessor :controller,
+ :action,
+ :method,
+ :format,
+ :models
+
+ def initialize
+ super()
+ @models = Set.new
+ yield self if block_given?
+ end
+
+ def signature
+ @signature ||= "#{controller.to_s}_#{action.to_s}_#{method.to_s}_#{format.to_s}"
+ end
+
+ def trackable?
+ !( method || '' ).to_s.match( GET ).nil?
+ end
+
+ def <<( model )
+ if model.is_a?( Array )
+ model, attribute = model
+ model = setup_model( model )
+ model << attribute
+ else
+ model = setup_model( model )
+ end
+ @models << model
+ end
+
+ def marshal_dump
+ { signature => { :controller => @controller,
+ :action => @action,
+ :method => @method,
+ :format => @format,
+ :models => dumped_models() } }
+ end
+
+ def marshal_load( data )
+ data = data.to_a.flatten.last
+ @controller = data[:controller]
+ @action = data[:action]
+ @method = data[:method]
+ @format = data[:format]
+ @models = data[:models]
+ self
+ end
+
+ def middlewares
+
+ end
+
+ def middleware
+ @middleware ||= begin
+ models.map do |model|
+ middleware_for_model( model )
+ end
+ end
+ end
+
+ def middleware_for_model( model )
+ resource = self
+ profile.orm.scope_resource_to_model( resource, model )
+ klass = Class.new
+ klass.class_eval(<<-EOS, __FILE__, __LINE__)
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ #{model.model.to_s}.#{profile.orm.resource_scope_method( resource ).to_s} do
+ @app.call(env)
+ end
+ end
+ EOS
+ klass
+ end
+
+ private
+
+ def dumped_models #:nodoc:
+ @models.to_a.map{|m| m.marshal_dump }
+ end
+
+ def setup_model( model )
+ if model.is_a?( Scrooge::Tracker::Model )
+ model
+ else
+ Scrooge::Tracker::Model.new( model )
+ end
+ end
+
+ end
+ end
+end
1  rails/init.rb
@@ -0,0 +1 @@
+require 'scrooge'
12 spec/fixtures/config/scrooge.yml
@@ -0,0 +1,12 @@
+production:
+ orm: 'active_record'
+ storage: 'memory'
+ buffer_threshold: 60
+ warmup_threshold: 300
+ scope: 1234567891
+test:
+ orm: 'active_record'
+ storage: 'memory'
+ buffer_threshold: 10
+ warmup_threshold: 30
+ scope:
25 spec/helpers/framework/rails/cache.rb
@@ -0,0 +1,25 @@
+module Spec
+ module Helpers
+ module Framework
+ module Rails
+ class Cache
+
+ attr_reader :storage
+
+ def initialize
+ @storage = {}
+ end
+
+ def read( key )
+ @storage[key]
+ end
+
+ def write( key, value )
+ @storage[key] = value
+ end
+
+ end
+ end
+ end
+ end
+end
52 spec/spec_helper.rb
@@ -0,0 +1,52 @@
+$:.unshift File.dirname(__FILE__) + '/../lib'
+
+require 'rubygems'
+require 'fileutils'
+require 'logger'
+require 'stringio'
+require 'lib/scrooge'
+require 'spec/helpers/framework/rails/cache'
+
+#ActiveRecord::Base.logger = Logger.new(StringIO.new)
+
+Spec::Runner.configure do |config|
+
+ Kernel.const_set :FIXTURES, "#{Dir.pwd}/spec/fixtures" unless defined?(FIXTURES)
+ Kernel.const_set :TMP, "#{Dir.pwd}/spec/tmp" unless defined?(TMP)
+ Kernel.const_set :CONFIG, "#{Dir.pwd}/spec/config" unless defined?(CONFIG)
+
+ config.before :all do
+ [TMP, CONFIG].each do |dir|
+ FileUtils.mkdir_p dir
+ end
+ end
+
+ config.before :each do
+ end
+
+ config.after :each do
+ end
+
+ config.after :all do
+ [TMP, CONFIG].each do |dir|
+ FileUtils.rm_r( dir ) rescue nil
+ end
+ end
+
+ def with_rails
+ begin
+ Kernel.const_set :RAILS_ROOT, "#{Dir.pwd}/spec" unless defined?(RAILS_ROOT)
+ Kernel.const_set :Rails, Class.new unless defined?(Rails)
+ Kernel.const_set :ActiveSupport, Class.new unless defined?(ActionView)
+ Kernel.const_set :ActionController, Class.new unless defined?(ActionController)
+ ::Rails.stub!(:cache).and_return( Spec::Helpers::Framework::Rails::Cache.new )
+ ::Rails.stub!(:root).and_return( RAILS_ROOT )
+ yield
+ ensure
+ [:RAILS_ROOT, :Rails, :ActiveSupport, :ActionController].each do |const|
+ Kernel.send( :remove_const, const )
+ end
+ end
+ end
+
+end
13 spec/units/scrooge/core/string_spec.rb
@@ -0,0 +1,13 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Core::String do
+
+ before(:each) do
+ @string = 'active_record'
+ end
+
+ it "should be able to convert itself to a constant" do
+ @string.to_const().should == 'ActiveRecord'
+ end
+
+end
13 spec/units/scrooge/core/symbol_spec.rb
@@ -0,0 +1,13 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Core::Symbol do
+
+ before(:each) do
+ @symbol = :active_record
+ end
+
+ it "should be able to convert itself to a constant" do
+ @symbol.to_const().should == 'ActiveRecord'
+ end
+
+end
99 spec/units/scrooge/framework/base_spec.rb
@@ -0,0 +1,99 @@
+require 'spec/spec_helper'
+
+describe "Scrooge::Framework::Base singleton" do
+
+ before(:each) do
+ @base = Scrooge::Framework::Base
+ end
+
+ it "should be able to track all available frameworks" do
+ @base.frameworks.should include( Scrooge::Framework::Rails )
+ end
+
+ it "should be able to infer the current active framework" do
+ lambda{ @base.which_framework? }.should raise_error( Scrooge::Framework::Base::NoSupportedFrameworks )
+ with_rails do
+ @base.which_framework?().should equal( Scrooge::Framework::Rails )
+ end
+ end
+
+ it "should be able to instantiate the active framework" do
+ with_rails do
+ @base.instantiate.class.should equal( Scrooge::Framework::Rails )
+ end
+ end
+
+end
+
+describe Scrooge::Framework::Base do
+
+ before(:each) do
+ @base = Scrooge::Framework::Base.new
+ end
+
+ it "should be able to yield it's environment" do
+ lambda{ @base.environment }.should raise_error( Scrooge::Framework::Base::NotImplemented )
+ end
+
+ it "should be able to yield it's root path" do
+ lambda{ @base.root }.should raise_error( Scrooge::Framework::Base::NotImplemented )
+ end
+
+ it "should be able to yield it's temp path" do
+ lambda{ @base.tmp }.should raise_error( Scrooge::Framework::Base::NotImplemented )
+ end
+
+ it "should be able to yield it's configuration path" do
+ lambda{ @base.config }.should raise_error( Scrooge::Framework::Base::NotImplemented )
+ end
+
+ it "should be able to yield it's logger" do
+ lambda{ @base.logger }.should raise_error( Scrooge::Framework::Base::NotImplemented )
+ end
+
+ it "should be able to read from the framework's cache store" do
+ lambda{ @base.read_cache( 'storage' ) }.should raise_error( Scrooge::Framework::Base::NotImplemented )
+ end
+
+ it "should be able to write to the framework's cache store" do
+ lambda{ @base.write_cache( 'storage', 'payload' ) }.should raise_error( Scrooge::Framework::Base::NotImplemented )
+ end
+
+ it "should be able to yield a scopes path" do
+ with_rails do
+ @base.stub!(:config).and_return( CONFIG )
+ @base.scopes_path.should match( /config\/scrooge\/scopes/ )
+ end
+ end
+
+ it "should be able to infer all available scopes" do
+ with_rails do
+ @base.stub!(:config).and_return( CONFIG )
+ @base.send( :ensure_scopes_path )
+ @signature = Time.now.to_i.to_s
+ FileUtils.mkdir_p( File.join( @base.scopes_path, @signature ) )
+ @base.scopes.should include( @signature )
+ end
+ end
+
+ it "should be able to infer the path to a given scope" do
+ with_rails do
+ @base.stub!(:config).and_return( CONFIG )
+ @base.scope_path( '1234567891' ).should match( /scopes\/1234567891/ )
+ end
+ end
+
+ it "should be able to interact with the framework's Rack middleware" do
+ lambda{ @base.middleware() }.should raise_error( Scrooge::Framework::Base::NotImplemented )
+ end
+
+ it "should be able to install tracking middleware" do
+ lambda{ @base.install_tracking_middleware() }.should raise_error( Scrooge::Framework::Base::NotImplemented )
+ end
+
+ it "should be able to install scoping middleware" do
+ lambda{ @base.install_scope_middleware( 'tracker' ) }.should raise_error( Scrooge::Framework::Base::NotImplemented )
+ end
+
+
+end
41 spec/units/scrooge/framework/rails_spec.rb
@@ -0,0 +1,41 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Framework::Rails do
+
+ before(:each) do
+ @framework = Scrooge::Framework::Rails.new
+ end
+
+ it "should be able to yield it's current environment" do
+ with_rails do
+ ::Rails.stub!(:env).and_return('production')
+ @framework.environment.should eql( 'production' )
+ end
+ end
+
+ it "should be able to yield a logger" do
+ with_rails do
+ ::Rails.stub!(:logger).and_return('')
+ ( @framework.logger << 'entry' ).should eql( 'entry' )
+ end
+ end
+
+ it "should be able to yield it's configuration" do
+ with_rails do
+ @framework.config.should match( /scrooge\/spec\/config/ )
+ end
+ end
+
+ it "should be able to yield it's root path" do
+ with_rails do
+ @framework.root.should match( /scrooge\/spec/ )
+ end
+ end
+
+ it "should be able to yield it's tmp path" do
+ with_rails do
+ @framework.tmp.should match( /scrooge\/spec\/tmp/ )
+ end
+ end
+
+end
57 spec/units/scrooge/orm/base_spec.rb
@@ -0,0 +1,57 @@
+require 'spec/spec_helper'
+
+describe "Scrooge::Orm::Base singleton" do
+
+ before(:each) do
+ @base = Scrooge::Orm::Base
+ end
+
+ it "should be able to instantiate a supported ORM from a given ORM signature" do
+ @base.instantiate( :active_record ).class.should equal(Scrooge::Orm::ActiveRecord)
+ end
+
+end
+
+describe Scrooge::Orm::Base do
+
+ before(:each) do
+ @tracker = mock('tracker')
+ @tracker.stub!(:signature).and_return( 'tracker' )
+ @base = Scrooge::Orm::Base.new
+ end
+
+ it "should be able to scope to a given resource tracker" do
+ lambda{ @base.scope_to( 'resource_tracker_instance' ) }.should raise_error( Scrooge::Orm::Base::NotImplemented )
+ end
+
+ it "should be able to generate a name for a given model" do
+ lambda{ @base.name( 'model' ) }.should raise_error( Scrooge::Orm::Base::NotImplemented )
+ end
+
+ it "should be able to generate a table name for a given model" do
+ lambda{ @base.table_name( 'model' ) }.should raise_error( Scrooge::Orm::Base::NotImplemented )
+ end
+
+ it "should be able to generate a per resource scope method" do
+ @base.resource_scope_method( @tracker ).should eql( :scope_to_tracker )
+ end
+
+ it "should be able to determine if a given scope method has already been defined" do
+ @base.resource_scope_method?( @tracker ).should equal( false )
+ end
+
+ it "should be able to infer the current trackable resource" do
+ Thread.current[:scrooge_resource] = 'resource'
+ @base.resource().should eql( 'resource' )
+ Thread.current[:scrooge_resource] = nil
+ end
+
+ it "should be able to determine if it's within the context of a trackable resource" do
+ @base.resource?().should eql( false )
+ end
+
+ it "should be able to determine if it should track attribute access" do
+ @base.track?().should equal( false )
+ end
+
+end
65 spec/units/scrooge/profile_spec.rb
@@ -0,0 +1,65 @@
+require 'spec/spec_helper'
+
+describe "Scrooge::Profile singleton" do
+
+ before(:each) do
+ @profile = Scrooge::Profile
+ end
+
+ it "should be able to instantiate it self from a given config path and environment" do
+ @profile.setup( File.join( FIXTURES, 'config', 'scrooge.yml' ), :production ).class.should equal( Scrooge::Profile )
+ @profile.setup( File.join( FIXTURES, 'config', 'scrooge.yml' ), :test ).options['orm'].should == 'active_record'
+ end
+
+end
+
+describe "Scrooge::Profile instance" do
+
+ before(:each) do
+ @profile = Scrooge::Profile.setup( File.join( FIXTURES, 'config', 'scrooge.yml' ), :production )
+ end
+
+ it "should be able to determine if a warmup is desireable" do
+ @profile.should be_warmed_up
+ end
+
+ it "should be able to determine if storage should be buffered" do
+ @profile.should be_buffered
+ end
+
+ it "should return a valid warmup threshold" do
+ @profile.warmup_threshold.should eql(300)
+ end
+
+ it "should return a valid buffer threshold" do
+ @profile.buffer_threshold.should eql(60)
+ end
+
+ it "should return a valid ORM instance" do
+ @profile.orm.class.should equal( Scrooge::Orm::ActiveRecord )
+ end
+
+ it "should return a valid storage instance" do
+ @profile.storage.class.should equal( Scrooge::Storage::Memory )
+ end
+
+ it "should return a valid framework instance" do
+ with_rails do
+ @profile.framework.class.should equal( Scrooge::Framework::Rails )
+ end
+ end
+
+ it "should return a valid tracker instance" do
+ @profile.tracker.class.should equal( Scrooge::Tracker::App )
+ end
+
+ it "should be able to determine if the active profile should track or scope" do
+ @profile.track?().should equal( false )
+ @profile.scope?().should equal( true )
+ end
+
+ it "should be able to infer the current scope" do
+ @profile.scope_to.should eql( 1234567891 )
+ end
+
+end
67 spec/units/scrooge/storage/base_spec.rb
@@ -0,0 +1,67 @@
+require 'spec/spec_helper'
+
+describe "Scrooge::Storage::Base singleton" do
+
+ before(:each) do
+ @base = Scrooge::Storage::Base
+ end
+
+ it "should be able to instantiate a storage backend from a given storage signature" do
+ @base.instantiate( :memory ).class.should equal(Scrooge::Storage::Memory)
+ end
+
+end
+
+describe Scrooge::Storage::Base do
+
+ before(:each) do
+ @base = Scrooge::Storage::Base.new
+ @tracker = mock('tracker')
+ @tracker.stub!(:signature).and_return('signature')
+ end
+
+ it "should initialize with a buffered at timestamp" do
+ @base.buffered_at.class.should equal( Bignum )
+ end
+
+ it "should initialize with an empty storage buffer" do
+ @base.storage_buffer.should eql( {} )
+ end
+
+ it "should be able to determine if it should buffer storage" do
+ @base.buffer?().should eql( false )
+ end
+
+ it "should be able to read from the storage backend" do
+ lambda{ @base.read( @tracker ) }.should raise_error( Scrooge::Storage::Base::NotImplemented )
+ end
+
+ it "should be able to write to the storage backend" do
+ lambda{ @base.write( @tracker ) }.should raise_error( Scrooge::Storage::Base::NotImplemented )
+ end
+
+ it "should be able to determine if it should flush it's buffer" do
+ @base.flush_buffer?().should eql( false )
+ @base.stub!(:buffer?).and_return(true)
+ @base.flush_buffer?().should eql( false )
+ @base.profile.stub!(:buffer_threshold).and_return(-10)
+ @base.flush_buffer?().should eql( true )
+ end
+
+ it "should be able to buffer a tracker" do
+ lambda { @base.buffer( @tracker ) }.should change( @base.storage_buffer, :size ).from(0).to(1)
+ @base.storage_buffer['signature'].should eql( @tracker )
+ end
+
+ it "should be able to yield a namespaced storage key" do
+ @base.expand_key( "signature" ).should eql( "scrooge_storage/signature" )
+ end
+
+ it "should be able to flush all storage buffers" do
+ @base.buffer( @tracker )
+ @base.stub!(:write).and_return(@tracker)
+ lambda { @base.flush! }.should change( @base.storage_buffer, :size ).from(1).to(0)
+ @base.storage_buffer.should be_empty
+ end
+
+end
24 spec/units/scrooge/storage/cache_spec.rb
@@ -0,0 +1,24 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Storage::Cache do
+
+ before(:each) do
+ @cache = Scrooge::Storage::Cache.new
+ @tracker = mock('tracker')
+ @tracker.stub!(:signature).and_return('signature')
+ end
+
+ it "should be able to write a tracker to the framework's cache store" do
+ with_rails do
+ lambda { @cache.write( @tracker ) }.should change( Rails.cache.storage, :size ).from(0).to(1)
+ end
+ end
+
+ it "should be able to read itself from the framework's cache store" do
+ with_rails do
+ @cache.write( @tracker )
+ @cache.read( @tracker ).should eql( @tracker )
+ end
+ end
+
+end
36 spec/units/scrooge/storage/file_system_spec.rb
@@ -0,0 +1,36 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Storage::FileSystem do
+
+ before(:each) do
+ @file_system = Scrooge::Storage::FileSystem.new
+ @tracker = mock('tracker')
+ @tracker.stub!(:signature).and_return('signature')
+ @framework = mock('framework')
+ @framework.stub!(:tmp).and_return( TMP )
+ @framework.stub!(:logger).and_return Logger.new( STDOUT )
+ @file_system.profile.stub!(:framework).and_return( @framework )
+ @file_system.stub!(:buffer?).and_return(false)
+ Marshal.stub!(:dump).and_return('tracker')
+ Marshal.stub!(:load).and_return('tracker')
+ end
+
+ it "should be able to compute a tracker directory path" do
+ @file_system.tracker_path( @tracker ).should match( /tmp\/signature/ )
+ end
+
+ it "should be able to compute a tracker file path" do
+ @file_system.tracker_file( @tracker ).should match( /tmp\/signature\/scrooge/ )
+ end
+
+ it "should be able to write a tracker to disk" do
+ @file_system.write( @tracker )
+ File.exist?( @file_system.tracker_file( @tracker ) ).should equal( true )
+ end
+
+ it "should be able to read a tracker from disk" do
+ @file_system.write( @tracker )
+ @file_system.read( @tracker ).should eql( 'tracker' )
+ end
+
+end
20 spec/units/scrooge/storage/memory_spec.rb
@@ -0,0 +1,20 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Storage::Memory do
+
+ before(:each) do
+ @memory = Scrooge::Storage::Memory.new
+ @tracker = mock('tracker')
+ @tracker.stub!(:signature).and_return('signature')
+ end
+
+ it "should be able to write a tracker to memory" do
+ lambda { @memory.write( @tracker ) }.should change( @memory.storage, :size ).from(0).to(1)
+ end
+
+ it "should be able to read itself from memory" do
+ @memory.write( @tracker )
+ @memory.read( @tracker ).should eql( @tracker )
+ end
+
+end
50 spec/units/scrooge/tracker/app_spec.rb
@@ -0,0 +1,50 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Tracker::App do
+
+ before(:each) do
+ @app = Scrooge::Tracker::App.new
+ @resource = Scrooge::Tracker::Resource.new do |resource|
+ resource.controller = 'products'
+ resource.action = 'show'
+ resource.method = :get
+ resource.format = :html
+ end
+ end
+
+ it "should initialize with an empty set of resources" do
+ @app.resources.should eql( Set.new )
+ end
+
+ it "should be able to accept resources" do
+ lambda { @app << 'a_resource' }.should change( @app.resources, :size ).from(0).to(1)
+ end
+
+ it "should be able to dump itself to a serializeable representation" do
+ @app << @resource
+ with_rails do
+ ::Rails.stub!(:env).and_return( 'test' )
+ @app.marshal_dump().should eql( { "test" => [ { "products_show_get_html" => { :method => :get,
+ :models => [],
+ :format => :html,
+ :action => "show",
+ :controller => "products" } } ] } )
+ end
+ end
+
+ it "should be able to restore itself from a serialized representation" do
+ @app << @resource
+ with_rails do
+ ::Rails.stub!(:env).and_return( 'test' )
+ Marshal.load( Marshal.dump( @app ) ).should eql( @app )
+ end
+ end
+
+ specify "should be able to compare itself to other app trackers" do
+ with_rails do
+ ::Rails.stub!(:env).and_return( 'test' )
+ @app.should eql( @app )
+ end
+ end
+
+end
21 spec/units/scrooge/tracker/base_spec.rb
@@ -0,0 +1,21 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Tracker::Base do
+
+ before(:each) do
+ @base = Scrooge::Tracker::Base.new
+ end
+
+ it "should have a numeric representation" do
+ @base.to_i.should equal(0)
+ end
+
+ it "should be able to dump itself to serializeable representation" do
+ lambda{ @base.marshal_dump }.should raise_error( Scrooge::Tracker::Base::NotImplemented )
+ end
+
+ it "should be able to restore itself from a serializeable representation" do
+ lambda{ @base.marshal_load( '' ) }.should raise_error( Scrooge::Tracker::Base::NotImplemented )
+ end
+
+end
39 spec/units/scrooge/tracker/model_spec.rb
@@ -0,0 +1,39 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Tracker::Model do
+
+ before(:each) do
+ @model = Scrooge::Tracker::Model.new( 'Post' )
+ @model.stub!(:name).and_return( 'Product' )
+ @model.stub!(:table_name).and_return( 'products' )
+ end
+
+ it "should initialize with an empty set of attributes" do
+ @model.attributes.should eql( Set.new )
+ end
+
+ it "should be able to accept attributes" do
+ lambda { @model << :name }.should change( @model.attributes, :size ).from(0).to(1)
+ end
+
+ it "should be able to dump itself to a serializeable representation" do
+ @model << [:name, :description, :price]
+ @model.marshal_dump().should eql( { "Product" => [:price, :description, :name] } )
+ end
+
+ it "should be able to restore itself from a serialized representation" do
+ @model << [:name, :description, :price]
+ lambda{ @model.marshal_load( { "Product" => [:price] } ) }.should change( @model.attributes, :size ).from(3).to(1)
+ end
+
+ it "should be able to render a attribute selection SQL snippet from it's referenced attributes" do
+ @model << [:name, :description, :price]
+ @model.to_sql().should eql( "products.price, products.description, products.name" )
+ end
+
+ specify "should be able to compare itself to other model trackers" do
+ @model << [:name, :description, :price]
+ @model.should eql( @model )
+ end
+
+end
69 spec/units/scrooge/tracker/resource_spec.rb
@@ -0,0 +1,69 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Tracker::Resource do
+
+ before(:each) do
+ @resource = Scrooge::Tracker::Resource.new do |resource|
+ resource.controller = 'products'
+ resource.action = 'show'
+ resource.method = :get
+ resource.format = :html
+ end
+ @model = Scrooge::Tracker::Model.new( 'Post' )
+ @model.stub!(:name).and_return( 'Product' )
+ @model.stub!(:table_name).and_return( 'products' )
+ end
+
+ it "should initialize with an empty set of models" do
+ @resource.models.should eql( Set.new )
+ end
+
+ it "should be able to accept models" do
+ lambda { @resource << @model }.should change( @resource.models, :size ).from(0).to(1)
+ end
+
+ it "should be able to wrap model objects" do
+ @resource << 'Model'
+ @resource.models.all?{|m| m.is_a?( Scrooge::Tracker::Model ) }.should equal( true )
+ end
+
+ it "should be able to log an attribute access for a given model" do
+ @resource << ['Post', :title]
+ @resource.models.first.attributes.should eql( Set[:title] )
+ end
+
+ it "should be able to determine if it's trackable" do
+ @resource.trackable?().should equal( true )
+ @resource.stub!(:method).and_return( :put )
+ @resource.trackable?().should equal( false )
+ end
+
+ it "should be able to generate a lookup signature" do
+ @resource.signature().should eql( "products_show_get_html" )
+ end
+
+ it "should be able to dump itself to a serializeable representation" do
+ @resource.marshal_dump().should eql( { "products_show_get_html" => { :models => [],
+ :method => :get,
+ :format => :html,
+ :action => "show",
+ :controller => "products"} } )
+ end
+
+ it "should be able to restore itself from a serialized representation" do
+ Marshal.load( Marshal.dump( @resource ) ).should eql( @resource )
+ end
+
+ it "should be able to compare itself to other resource trackers" do
+ @resource.should eql( @resource )
+ end
+
+ it "should be able to setup Rack middleware" do
+ @resource.profile.orm.stub!(:resource_scope_method).and_return(:scoped_to_resource_method)
+ @resource << @model
+ @resource.middleware().class.should equal( Array )
+ @resource.middleware().first.class.should equal( Class )
+ @resource.middleware().first.new( @model ).should respond_to( :call )
+ end
+
+end
13 spec/units/scrooge_spec.rb
@@ -0,0 +1,13 @@
+require 'spec/spec_helper'
+
+describe Scrooge::Base do
+
+ before(:each) do
+ @base = Scrooge::Base
+ end
+
+ it "should be able to instantiate it's profile" do
+ @base.profile.class.should == Scrooge::Profile
+ end
+
+end
Please sign in to comment.
Something went wrong with that request. Please try again.