brainguy 0.0.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.
- checksums.yaml +7 -0
 - data/.gitignore +15 -0
 - data/.yardopts +4 -0
 - data/Gemfile +4 -0
 - data/LICENSE.txt +22 -0
 - data/README.erb +345 -0
 - data/README.markdown +579 -0
 - data/Rakefile +21 -0
 - data/brainguy.gemspec +28 -0
 - data/examples/include_manifestly_observable.rb +22 -0
 - data/examples/include_observable.rb +18 -0
 - data/examples/include_observer.rb +36 -0
 - data/examples/manual_observable.rb +22 -0
 - data/examples/open_observer.rb +31 -0
 - data/examples/proc_observer.rb +10 -0
 - data/examples/scoped_subscription.rb +39 -0
 - data/examples/synopsis.rb +56 -0
 - data/lib/brainguy.rb +34 -0
 - data/lib/brainguy/basic_notifier.rb +19 -0
 - data/lib/brainguy/emitter.rb +110 -0
 - data/lib/brainguy/error_collecting_notifier.rb +26 -0
 - data/lib/brainguy/error_handling_notifier.rb +63 -0
 - data/lib/brainguy/event.rb +13 -0
 - data/lib/brainguy/fluent_emitter.rb +30 -0
 - data/lib/brainguy/full_subscription.rb +8 -0
 - data/lib/brainguy/idempotent_emitter.rb +40 -0
 - data/lib/brainguy/manifest_emitter.rb +78 -0
 - data/lib/brainguy/manifestly_observable.rb +62 -0
 - data/lib/brainguy/observable.rb +33 -0
 - data/lib/brainguy/observer.rb +71 -0
 - data/lib/brainguy/open_observer.rb +65 -0
 - data/lib/brainguy/single_event_subscription.rb +31 -0
 - data/lib/brainguy/subscription.rb +59 -0
 - data/lib/brainguy/subscription_scope.rb +62 -0
 - data/lib/brainguy/version.rb +4 -0
 - data/scripts/benchmark_listener_dispatch.rb +222 -0
 - data/spec/brainguy/emitter_spec.rb +25 -0
 - data/spec/brainguy/error_collecting_notifier_spec.rb +19 -0
 - data/spec/brainguy/error_handling_notifier_spec.rb +63 -0
 - data/spec/brainguy/manifest_emitter_spec.rb +68 -0
 - data/spec/brainguy/manifestly_observable_spec.rb +43 -0
 - data/spec/brainguy/observable_spec.rb +9 -0
 - data/spec/brainguy/observer_spec.rb +72 -0
 - data/spec/brainguy/open_observer_spec.rb +57 -0
 - data/spec/brainguy/single_event_subscription_spec.rb +16 -0
 - data/spec/brainguy/subscription_scope_spec.rb +72 -0
 - data/spec/brainguy/subscription_spec.rb +46 -0
 - data/spec/features/basics_spec.rb +153 -0
 - data/spec/features/idempotent_events_spec.rb +69 -0
 - data/spec/features/method_scoped_events_spec.rb +90 -0
 - data/spec/support/shared_examples_for_eventful_modules.rb +36 -0
 - metadata +196 -0
 
    
        data/Rakefile
    ADDED
    
    | 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "bundler/gem_tasks"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "yard"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            task :build => :readme
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            desc "Build the README"
         
     | 
| 
      
 7 
     | 
    
         
            +
            task :readme => "README.markdown"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            file "README.markdown" => "README.erb" do
         
     | 
| 
      
 10 
     | 
    
         
            +
              puts "Generating README.markdown"
         
     | 
| 
      
 11 
     | 
    
         
            +
              require "erb"
         
     | 
| 
      
 12 
     | 
    
         
            +
              template = IO.read("README.erb")
         
     | 
| 
      
 13 
     | 
    
         
            +
              IO.write("README.markdown", ERB.new(template).result)
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            YARD::Rake::YardocTask.new do |t|
         
     | 
| 
      
 17 
     | 
    
         
            +
              # t.files   = ['lib/**/*.rb', OTHER_PATHS]   # optional
         
     | 
| 
      
 18 
     | 
    
         
            +
              # t.options = ['--any', '--extra', '--opts'] # optional
         
     | 
| 
      
 19 
     | 
    
         
            +
              # t.stats_options = ['--list-undoc']         # optional
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
      
 21 
     | 
    
         
            +
            task :yard => :readme
         
     | 
    
        data/brainguy.gemspec
    ADDED
    
    | 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # coding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            lib = File.expand_path('../lib', __FILE__)
         
     | 
| 
      
 3 
     | 
    
         
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'brainguy/version'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            Gem::Specification.new do |spec|
         
     | 
| 
      
 7 
     | 
    
         
            +
              spec.name          = "brainguy"
         
     | 
| 
      
 8 
     | 
    
         
            +
              spec.version       = Brainguy::VERSION
         
     | 
| 
      
 9 
     | 
    
         
            +
              spec.authors       = ["Avdi Grimm"]
         
     | 
| 
      
 10 
     | 
    
         
            +
              spec.email         = ["avdi@avdi.org"]
         
     | 
| 
      
 11 
     | 
    
         
            +
              spec.summary       = %q{An Observer pattern library}
         
     | 
| 
      
 12 
     | 
    
         
            +
              spec.description   = %q{A somewhat fancy observer pattern library with
         
     | 
| 
      
 13 
     | 
    
         
            +
            features like named events and scoped subscriptions.}
         
     | 
| 
      
 14 
     | 
    
         
            +
              spec.homepage      = "https://github.com/avdi/brainguy"
         
     | 
| 
      
 15 
     | 
    
         
            +
              spec.license       = "MIT"
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              spec.files         = `git ls-files -z`.split("\x0")
         
     | 
| 
      
 18 
     | 
    
         
            +
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         
     | 
| 
      
 19 
     | 
    
         
            +
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         
     | 
| 
      
 20 
     | 
    
         
            +
              spec.require_paths = ["lib"]
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              spec.add_development_dependency "bundler", "~> 1.7"
         
     | 
| 
      
 23 
     | 
    
         
            +
              spec.add_development_dependency "rake", "~> 10.0"
         
     | 
| 
      
 24 
     | 
    
         
            +
              spec.add_development_dependency "rspec", "~> 3.2"
         
     | 
| 
      
 25 
     | 
    
         
            +
              spec.add_development_dependency "benchmark-ips"
         
     | 
| 
      
 26 
     | 
    
         
            +
              spec.add_development_dependency "yard", "~> 0.8.7"
         
     | 
| 
      
 27 
     | 
    
         
            +
              spec.add_development_dependency "seeing_is_believing", "~> 2.2"
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "brainguy"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Toaster
         
     | 
| 
      
 4 
     | 
    
         
            +
              include Brainguy::ManifestlyObservable.new(:start, :pop)
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def make_toast
         
     | 
| 
      
 7 
     | 
    
         
            +
                emit(:start)
         
     | 
| 
      
 8 
     | 
    
         
            +
                emit(:lop)
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            toaster = Toaster.new
         
     | 
| 
      
 13 
     | 
    
         
            +
            toaster.events.unknown_event_policy = :raise_error
         
     | 
| 
      
 14 
     | 
    
         
            +
            toaster.on(:plop) do
         
     | 
| 
      
 15 
     | 
    
         
            +
              puts "Toast is done!"
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
      
 17 
     | 
    
         
            +
            toaster.make_toast
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            # ~> Brainguy::UnknownEvent
         
     | 
| 
      
 20 
     | 
    
         
            +
            # ~> #on received for unknown event type 'plop'
         
     | 
| 
      
 21 
     | 
    
         
            +
            # ~>
         
     | 
| 
      
 22 
     | 
    
         
            +
            # ~> xmptmp-in27856uxq.rb:14:in `<main>'
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "brainguy"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Toaster
         
     | 
| 
      
 4 
     | 
    
         
            +
              include Brainguy::Observable
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def make_toast
         
     | 
| 
      
 7 
     | 
    
         
            +
                emit(:start)
         
     | 
| 
      
 8 
     | 
    
         
            +
                emit(:pop)
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            toaster = Toaster.new
         
     | 
| 
      
 13 
     | 
    
         
            +
            toaster.on(:pop) do
         
     | 
| 
      
 14 
     | 
    
         
            +
              puts "Toast is done!"
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
      
 16 
     | 
    
         
            +
            toaster.make_toast
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            # >> Toast is done!
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "brainguy"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Poem
         
     | 
| 
      
 4 
     | 
    
         
            +
              include Brainguy::Observable
         
     | 
| 
      
 5 
     | 
    
         
            +
              def recite
         
     | 
| 
      
 6 
     | 
    
         
            +
                emit(:title, "Jabberwocky")
         
     | 
| 
      
 7 
     | 
    
         
            +
                emit(:line, "'twas brillig, and the slithy toves")
         
     | 
| 
      
 8 
     | 
    
         
            +
                emit(:line, "Did gyre and gimbal in the wabe")
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            class HtmlFormatter
         
     | 
| 
      
 13 
     | 
    
         
            +
              include Brainguy::Observer
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              attr_reader :result
         
     | 
| 
      
 16 
     | 
    
         
            +
              
         
     | 
| 
      
 17 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 18 
     | 
    
         
            +
                @result = ""
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
              
         
     | 
| 
      
 21 
     | 
    
         
            +
              def on_title(event)
         
     | 
| 
      
 22 
     | 
    
         
            +
                @result << "<h1>#{event.args.first}</h1>"
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              def on_line(event)
         
     | 
| 
      
 26 
     | 
    
         
            +
                @result << "#{event.args.first}</br>"
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            p = Poem.new
         
     | 
| 
      
 31 
     | 
    
         
            +
            f = HtmlFormatter.new
         
     | 
| 
      
 32 
     | 
    
         
            +
            p.events.attach(f)
         
     | 
| 
      
 33 
     | 
    
         
            +
            p.recite
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            f.result
         
     | 
| 
      
 36 
     | 
    
         
            +
            # => "<h1>Jabberwocky</h1>'twas brillig, and the slithy toves</br>Did gyre an...
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "brainguy"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Toaster
         
     | 
| 
      
 4 
     | 
    
         
            +
              attr_reader :events
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 7 
     | 
    
         
            +
                @events = Brainguy::Emitter.new(self)
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def make_toast
         
     | 
| 
      
 11 
     | 
    
         
            +
                events.emit(:start)
         
     | 
| 
      
 12 
     | 
    
         
            +
                events.emit(:pop)
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            toaster = Toaster.new
         
     | 
| 
      
 17 
     | 
    
         
            +
            toaster.events.on(:pop) do
         
     | 
| 
      
 18 
     | 
    
         
            +
              puts "Toanst is done!"
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
      
 20 
     | 
    
         
            +
            toaster.make_toast
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            # >> Toast is done!
         
     | 
| 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "brainguy"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class VideoRender
         
     | 
| 
      
 4 
     | 
    
         
            +
              include Brainguy::Observable
         
     | 
| 
      
 5 
     | 
    
         
            +
              attr_reader :name
         
     | 
| 
      
 6 
     | 
    
         
            +
              def initialize(name)
         
     | 
| 
      
 7 
     | 
    
         
            +
                @name = name
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
              
         
     | 
| 
      
 10 
     | 
    
         
            +
              def do_render
         
     | 
| 
      
 11 
     | 
    
         
            +
                emit(:complete)
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            v1 = VideoRender.new("foo.mp4")
         
     | 
| 
      
 16 
     | 
    
         
            +
            v2 = VideoRender.new("bar.mp4")
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            observer = Brainguy::OpenObserver.new do |o|
         
     | 
| 
      
 19 
     | 
    
         
            +
              o.on_complete do |event|
         
     | 
| 
      
 20 
     | 
    
         
            +
                puts "Video #{event.source.name} is done rendering!"
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            v1.events.attach(observer)
         
     | 
| 
      
 25 
     | 
    
         
            +
            v2.events.attach(observer)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            v1.do_render
         
     | 
| 
      
 28 
     | 
    
         
            +
            v2.do_render
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            # >> Video foo.mp4 is done rendering!
         
     | 
| 
      
 31 
     | 
    
         
            +
            # >> Video bar.mp4 is done rendering!
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "brainguy"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Poem
         
     | 
| 
      
 4 
     | 
    
         
            +
              include Brainguy::Observable
         
     | 
| 
      
 5 
     | 
    
         
            +
              def recite(&block)
         
     | 
| 
      
 6 
     | 
    
         
            +
                with_subscription_scope(block) do
         
     | 
| 
      
 7 
     | 
    
         
            +
                  emit(:title, "Jabberwocky")
         
     | 
| 
      
 8 
     | 
    
         
            +
                  emit(:line, "'twas brillig, and the slithy toves")
         
     | 
| 
      
 9 
     | 
    
         
            +
                  emit(:line, "Did gyre and gimbal in the wabe")
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            class HtmlFormatter
         
     | 
| 
      
 15 
     | 
    
         
            +
              include Brainguy::Observer
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              attr_reader :result
         
     | 
| 
      
 18 
     | 
    
         
            +
              
         
     | 
| 
      
 19 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 20 
     | 
    
         
            +
                @result = ""
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
              
         
     | 
| 
      
 23 
     | 
    
         
            +
              def on_title(event)
         
     | 
| 
      
 24 
     | 
    
         
            +
                @result << "<h1>#{event.args.first}</h1>"
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              def on_line(event)
         
     | 
| 
      
 28 
     | 
    
         
            +
                @result << "#{event.args.first}</br>"
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            p = Poem.new
         
     | 
| 
      
 33 
     | 
    
         
            +
            f = HtmlFormatter.new
         
     | 
| 
      
 34 
     | 
    
         
            +
            p.recite do |events|
         
     | 
| 
      
 35 
     | 
    
         
            +
              events.attach(f)
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            f.result
         
     | 
| 
      
 39 
     | 
    
         
            +
            # => "<h1>Jabberwocky</h1>'twas brillig, and the slithy toves</br>Did gyre an...
         
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "brainguy"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class SatelliteOfLove
         
     | 
| 
      
 4 
     | 
    
         
            +
              include Brainguy::Observable
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def intro_song
         
     | 
| 
      
 7 
     | 
    
         
            +
                emit(:robot_roll_call)
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def send_the_movie
         
     | 
| 
      
 11 
     | 
    
         
            +
                emit(:movie_sign)
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            class Crew
         
     | 
| 
      
 16 
     | 
    
         
            +
              include Brainguy::Observer
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            class TomServo < Crew
         
     | 
| 
      
 20 
     | 
    
         
            +
              def on_robot_roll_call(event)
         
     | 
| 
      
 21 
     | 
    
         
            +
                puts "Tom: Check me out!"
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            class CrowTRobot < Crew
         
     | 
| 
      
 26 
     | 
    
         
            +
              def on_robot_roll_call(event)
         
     | 
| 
      
 27 
     | 
    
         
            +
                puts "Crow: I'm different!"
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            class MikeNelson < Crew
         
     | 
| 
      
 32 
     | 
    
         
            +
              def on_movie_sign(event)
         
     | 
| 
      
 33 
     | 
    
         
            +
                puts "Mike: Oh no we've got movie sign!"
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            sol = SatelliteOfLove.new
         
     | 
| 
      
 38 
     | 
    
         
            +
            # Attach specific event handlers without a listener object
         
     | 
| 
      
 39 
     | 
    
         
            +
            sol.on(:robot_roll_call) do
         
     | 
| 
      
 40 
     | 
    
         
            +
              puts "[Robot roll call!]"
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
      
 42 
     | 
    
         
            +
            sol.on(:movie_sign) do
         
     | 
| 
      
 43 
     | 
    
         
            +
              puts "[Movie sign flashes]"
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
| 
      
 45 
     | 
    
         
            +
            sol.events.attach TomServo.new
         
     | 
| 
      
 46 
     | 
    
         
            +
            sol.events.attach CrowTRobot.new
         
     | 
| 
      
 47 
     | 
    
         
            +
            sol.events.attach MikeNelson.new
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            sol.intro_song
         
     | 
| 
      
 50 
     | 
    
         
            +
            sol.send_the_movie
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            # >> [Robot roll call!]
         
     | 
| 
      
 53 
     | 
    
         
            +
            # >> Tom: Check me out!
         
     | 
| 
      
 54 
     | 
    
         
            +
            # >> Crow: I'm different!
         
     | 
| 
      
 55 
     | 
    
         
            +
            # >> [Movie sign flashes]
         
     | 
| 
      
 56 
     | 
    
         
            +
            # >> Mike: Oh no we've got movie sign!
         
     | 
    
        data/lib/brainguy.rb
    ADDED
    
    | 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "brainguy/version"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "brainguy/event"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "brainguy/emitter"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "brainguy/idempotent_emitter"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "brainguy/observable"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "brainguy/manifestly_observable"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require "brainguy/subscription_scope"
         
     | 
| 
      
 9 
     | 
    
         
            +
            require "brainguy/fluent_emitter"
         
     | 
| 
      
 10 
     | 
    
         
            +
            require "brainguy/observer"
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            # Namespace for the `brainguy` gem. See {file:README.md} for usage instructions.
         
     | 
| 
      
 13 
     | 
    
         
            +
            module Brainguy
         
     | 
| 
      
 14 
     | 
    
         
            +
              # Execute passed block with a temporary subscription scope. See README for
         
     | 
| 
      
 15 
     | 
    
         
            +
              # examples.
         
     | 
| 
      
 16 
     | 
    
         
            +
              #
         
     | 
| 
      
 17 
     | 
    
         
            +
              # @param source   the object initiating the event
         
     | 
| 
      
 18 
     | 
    
         
            +
              # @param listener_block [:call] an optional callable that should hook up
         
     | 
| 
      
 19 
     | 
    
         
            +
              #   listeners
         
     | 
| 
      
 20 
     | 
    
         
            +
              # @param subscription_set [Emitter] an existing subscription set to
         
     | 
| 
      
 21 
     | 
    
         
            +
              #   layer on top of
         
     | 
| 
      
 22 
     | 
    
         
            +
              def self.with_subscription_scope(
         
     | 
| 
      
 23 
     | 
    
         
            +
                  source,
         
     | 
| 
      
 24 
     | 
    
         
            +
                  listener_block   = nil,
         
     | 
| 
      
 25 
     | 
    
         
            +
                  subscription_set = IdempotentEmitter.new(source))
         
     | 
| 
      
 26 
     | 
    
         
            +
                subscription_set.with_subscription_scope do |scope|
         
     | 
| 
      
 27 
     | 
    
         
            +
                  listener_block.call(scope) if listener_block
         
     | 
| 
      
 28 
     | 
    
         
            +
                  yield scope
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
                unless listener_block
         
     | 
| 
      
 31 
     | 
    
         
            +
                  FluentEmitter.new(subscription_set)
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Brainguy
         
     | 
| 
      
 2 
     | 
    
         
            +
              # A notifier encapsulates various strategies for notifying subscriptions of
         
     | 
| 
      
 3 
     | 
    
         
            +
              # events. This is the most basic form of notifier. It just passes the event
         
     | 
| 
      
 4 
     | 
    
         
            +
              # on with no extra logic.
         
     | 
| 
      
 5 
     | 
    
         
            +
              class BasicNotifier
         
     | 
| 
      
 6 
     | 
    
         
            +
                # Notify a subscription of an event
         
     | 
| 
      
 7 
     | 
    
         
            +
                #
         
     | 
| 
      
 8 
     | 
    
         
            +
                # @return (see Subscription#handle)
         
     | 
| 
      
 9 
     | 
    
         
            +
                def notify(subscription, event)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  subscription.handle(event)
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                # Some notifiers have interesting results. This one just returns nil.
         
     | 
| 
      
 14 
     | 
    
         
            +
                # @return nil
         
     | 
| 
      
 15 
     | 
    
         
            +
                def result
         
     | 
| 
      
 16 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,110 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "delegate"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "set"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "brainguy/full_subscription"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "brainguy/single_event_subscription"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "brainguy/event"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "brainguy/basic_notifier"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require "brainguy/open_observer"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module Brainguy
         
     | 
| 
      
 10 
     | 
    
         
            +
              # This object keeps track of all the listeners (observers) subscribed to a
         
     | 
| 
      
 11 
     | 
    
         
            +
              # particular event source object.
         
     | 
| 
      
 12 
     | 
    
         
            +
              class Emitter < DelegateClass(Set)
         
     | 
| 
      
 13 
     | 
    
         
            +
                DEFAULT_NOTIFIER = BasicNotifier.new
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                # Create a new {Emitter} that shares its inner dataset with an
         
     | 
| 
      
 16 
     | 
    
         
            +
                # existing one. This exists so that it's possible to generate temporary
         
     | 
| 
      
 17 
     | 
    
         
            +
                # copies of a {Emitter} with different, specialized semantics;
         
     | 
| 
      
 18 
     | 
    
         
            +
                # for instance, an {IdempotentEmitter} that shares the same
         
     | 
| 
      
 19 
     | 
    
         
            +
                # set of subscriptions as an existing {Emitter}.
         
     | 
| 
      
 20 
     | 
    
         
            +
                # @param event_source [Object] the event-originating object
         
     | 
| 
      
 21 
     | 
    
         
            +
                # @param subscription_set [Emitter] the existing set to share
         
     | 
| 
      
 22 
     | 
    
         
            +
                #   subscriptions with
         
     | 
| 
      
 23 
     | 
    
         
            +
                # @return [Emitter]
         
     | 
| 
      
 24 
     | 
    
         
            +
                def self.new_from_existing(event_source, subscription_set)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  new(event_source, subscriptions: subscription_set.subscriptions)
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                # @param event_source [Object] the event-originating object
         
     | 
| 
      
 29 
     | 
    
         
            +
                # @option options [Set<Subscription>] :subscriptions (Set.new) the
         
     | 
| 
      
 30 
     | 
    
         
            +
                #   underlying set of subscriptions
         
     | 
| 
      
 31 
     | 
    
         
            +
                # @option options [:call] :notifier_maker a factory for notifiers.
         
     | 
| 
      
 32 
     | 
    
         
            +
                def initialize(event_source = self, options = {})
         
     | 
| 
      
 33 
     | 
    
         
            +
                  super(options[:subscriptions] || Set.new)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @event_source   = event_source
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @notifier_maker = options.fetch(:notifier_maker) {
         
     | 
| 
      
 36 
     | 
    
         
            +
                    ->() { DEFAULT_NOTIFIER }
         
     | 
| 
      
 37 
     | 
    
         
            +
                  }
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                # @return [Set<Subscription>] the underlying set of subscription objects
         
     | 
| 
      
 41 
     | 
    
         
            +
                def subscriptions
         
     | 
| 
      
 42 
     | 
    
         
            +
                  __getobj__
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                # Attach a new object to listen for events. A listener is expected to be
         
     | 
| 
      
 46 
     | 
    
         
            +
                # call-able, and it will receive the `#call` message with an {Event} each
         
     | 
| 
      
 47 
     | 
    
         
            +
                # time one is emitted.
         
     | 
| 
      
 48 
     | 
    
         
            +
                # @param new_listener [:call]
         
     | 
| 
      
 49 
     | 
    
         
            +
                # @return [Subscription] a subscription object which can be used to
         
     | 
| 
      
 50 
     | 
    
         
            +
                #   cancel the subscription.
         
     | 
| 
      
 51 
     | 
    
         
            +
                def attach(new_listener)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  FullSubscription.new(self, new_listener).tap do |subscription|
         
     | 
| 
      
 53 
     | 
    
         
            +
                    self << subscription
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                # Detach a listener. This locates the subscription corresponding to the
         
     | 
| 
      
 58 
     | 
    
         
            +
                # given listener (if any), and removes it.
         
     | 
| 
      
 59 
     | 
    
         
            +
                # @param [:call] listener a listener to be unsubscribed
         
     | 
| 
      
 60 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 61 
     | 
    
         
            +
                def detach(listener)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  delete(FullSubscription.new(self, listener))
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                # Attach blocks of code to handle specific named events.
         
     | 
| 
      
 66 
     | 
    
         
            +
                # @overload on(name, &block)
         
     | 
| 
      
 67 
     | 
    
         
            +
                #   Attach a block to be called for a specific event. The block will be
         
     | 
| 
      
 68 
     | 
    
         
            +
                #   called with the event arguments (not the event object).
         
     | 
| 
      
 69 
     | 
    
         
            +
                #   @param name [Symbol]
         
     | 
| 
      
 70 
     | 
    
         
            +
                #   @param block [Proc] what to do when the event is emitted
         
     | 
| 
      
 71 
     | 
    
         
            +
                # @overload on(handlers)
         
     | 
| 
      
 72 
     | 
    
         
            +
                #   Attach multiple event-specific handlers at once.
         
     | 
| 
      
 73 
     | 
    
         
            +
                #   @param handlers [Hash{Symbol => [:call]}] a map of event names to
         
     | 
| 
      
 74 
     | 
    
         
            +
                #     callable handlers.
         
     | 
| 
      
 75 
     | 
    
         
            +
                # @return (see #attach)
         
     | 
| 
      
 76 
     | 
    
         
            +
                def on(name_or_handlers, &block)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  case name_or_handlers
         
     | 
| 
      
 78 
     | 
    
         
            +
                  when Symbol
         
     | 
| 
      
 79 
     | 
    
         
            +
                    attach_to_single_event(name_or_handlers, block)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  when Hash
         
     | 
| 
      
 81 
     | 
    
         
            +
                    attach(OpenObserver.new(name_or_handlers))
         
     | 
| 
      
 82 
     | 
    
         
            +
                  else
         
     | 
| 
      
 83 
     | 
    
         
            +
                    fail ArgumentError, "Event name or Hash required"
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                # Emit an event to be distributed to all interested listeners.
         
     | 
| 
      
 88 
     | 
    
         
            +
                # @param event_name [Symbol] the name of the event
         
     | 
| 
      
 89 
     | 
    
         
            +
                # @param extra_args [Array] any extra arguments that should accompany the
         
     | 
| 
      
 90 
     | 
    
         
            +
                #   event
         
     | 
| 
      
 91 
     | 
    
         
            +
                # @return the notifier's result value
         
     | 
| 
      
 92 
     | 
    
         
            +
                def emit(event_name, *extra_args)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  notifier = @notifier_maker.call
         
     | 
| 
      
 94 
     | 
    
         
            +
                  each do |subscription|
         
     | 
| 
      
 95 
     | 
    
         
            +
                    event = Event.new(event_name, @event_source, extra_args)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    notifier.notify(subscription, event)
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
                  notifier.result
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                private
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                def attach_to_single_event(event_name, block)
         
     | 
| 
      
 104 
     | 
    
         
            +
                  SingleEventSubscription.new(self, block, event_name).tap do
         
     | 
| 
      
 105 
     | 
    
         
            +
                  |subscription|
         
     | 
| 
      
 106 
     | 
    
         
            +
                    self << subscription
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
                end
         
     | 
| 
      
 109 
     | 
    
         
            +
              end
         
     | 
| 
      
 110 
     | 
    
         
            +
            end
         
     |