lib/ruote/context.rb in ruote-2.2.0 vs lib/ruote/context.rb in ruote-2.3.0

- old
+ new

@@ -1,7 +1,7 @@ #-- -# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com +# Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell @@ -35,23 +35,20 @@ class Context SERVICE_PREFIX = /^s\_/ attr_reader :storage - attr_accessor :worker - attr_accessor :engine + attr_accessor :dashboard - def initialize(storage, worker=nil) + def initialize(storage) @storage = storage @storage.context = self - @engine = nil - @worker = worker + @dashboard = nil @services = {} - initialize_services end # A trick : in order to avoid # @@ -64,105 +61,188 @@ def context self end + # Let's make sure Context always responds to #storage, #dashboard (#engine) + # and #worker. + # + alias engine dashboard + + # Let's make sure Context always responds to #storage, #dashboard (#engine) + # and #worker. + # + def worker + + @services['s_worker'] + end + # Returns the engine_id (as set in the configuration under the key # "engine_id"), or, by default, "engine". # def engine_id - get_conf['engine_id'] || 'engine' + conf['engine_id'] || 'engine' end # Used for things like # # if @context['ruby_eval_allowed'] # # ... # end # def [](key) - SERVICE_PREFIX.match(key) ? @services[key] : get_conf[key] + if SERVICE_PREFIX.match(key) + @services[key] + else + conf[key] + end end # Mostly used by engine#configure # def []=(key, value) raise( ArgumentError.new('use context#add_service to register services') ) if SERVICE_PREFIX.match(key) - cf = get_conf - cf[key] = value - @storage.put(cf) + @storage.put(conf.merge(key => value)) + # TODO blindly trust the put ? retry in case of failure ? value end + # Configuration keys and service keys. + # def keys - get_conf.keys + (@services.keys + conf.keys).uniq.sort end + # Called by Ruote::Dashboard#add_service + # def add_service(key, *args) + raise ArgumentError.new( + '#add_service: at least two arguments please' + ) if args.empty? + + key = key.to_s path, klass, opts = args key = "s_#{key}" unless SERVICE_PREFIX.match(key) + aa = [ self, opts ].compact + service = if klass - require(path) if path + require(path) - aa = [ self ] - aa << opts if opts - @services[key] = Ruote.constantize(klass).new(*aa) + + elsif path.is_a?(Class) + + @services[key] = path.new(*aa) + else @services[key] = path end - self.class.class_eval %{ def #{key[2..-1]}; @services['#{key}']; end } - # - # This 'one-liner' will add an instance method to Context for this - # service. - # - # If the service key is 's_dishwasher', then the service will be - # available via Context#dishwasher. - # - # I.e. dishwasher = engine.context.dishwasher + (class << self; self; end).class_eval( + %{ def #{key[2..-1]}; @services['#{key}']; end }) + # + # This 'two-liner' will add an instance method to Context for this + # service. + # + # If the service key is 's_dishwasher', then the service will be + # available via Context#dishwasher. + # + # I.e. dishwasher = engine.context.dishwasher service end + # This is kind of evil. Notifies services responding to #on_pre_msg + # with the msg before it gets processed. + # + # Might be useful in some cases. Use with great care. + # + def pre_notify(msg) + + @services.select { |n, s| + s.respond_to?(:on_pre_msg) + }.sort_by { |n, s| + n + }.each { |n, s| + s.on_pre_msg(msg) + } + end + + # This method is called by the worker each time it sucessfully processed + # a msg. This method calls in turn the #on_msg method for each of the + # services (that respond to that method). + # + # Makes sure that observers that respond to #wait_for are called last. + # + def notify(msg) + + waiters, observers = @services.select { |n, s| + s.respond_to?(:on_msg) + }.sort_by { |n, s| + n + }.partition { |n, s| + s.respond_to?(:wait_for) + } + + (observers + waiters).each { |n, s| s.on_msg(msg) } + end + # Takes care of shutting down every service registered in this context. # def shutdown - @worker.shutdown if @worker - @storage.shutdown if @storage.respond_to?(:shutdown) + ([ @storage ] + @services.values).each do |s| + s.shutdown if s.respond_to?(:shutdown) + end + end - @services.values.each { |s| s.shutdown if s.respond_to?(:shutdown) } + alias engine dashboard + alias engine= dashboard= + + # Returns true if this context has a given service registered. + # + def has_service?(service_name) + + service_name = service_name.to_s + service_name = "s_#{service_name}" if ! SERVICE_PREFIX.match(service_name) + + @services.has_key?(service_name) end + # List of services in this context, sorted by their name in alphabetical + # order. + # + def services + + @services.keys.sort.collect { |k| @services[k] } + end + protected - def get_conf + def conf - @storage.get_configuration('engine') || {} + @storage.get_configuration('engine') end def initialize_services - default_conf.merge(get_conf).each do |key, value| + default_conf.merge(conf).each do |key, value| - next unless SERVICE_PREFIX.match(key) - - add_service(key, *value) + add_service(key, *value) if SERVICE_PREFIX.match(key) end end def default_conf @@ -186,9 +266,34 @@ 'ruote/svc/error_handler', 'Ruote::ErrorHandler' ], 's_logger' => [ 'ruote/log/wait_logger', 'Ruote::WaitLogger' ], 's_history' => [ 'ruote/log/default_history', 'Ruote::DefaultHistory' ] } + end + end + + # + # A minimal context, useful for testing expressions in isolation. + # + class TestContext < Context + + def initialize + + @services = {} + initialize_services + end + + protected + + def conf + + {} + end + + def default_conf + + { 's_dollar_sub' => [ + 'ruote/svc/dollar_sub', 'Ruote::DollarSubstitution' ] } end end end