exceptional_synchrony 1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6b536ff75b55f1cd20206a7d4b6befd78555c587
4
+ data.tar.gz: 9dcb17118c8e3be70aa63a9a1491f0d50d697c1e
5
+ SHA512:
6
+ metadata.gz: 894cf6ac51e3b063e87ad8c125721a3ea3fecdd07ba307aa7e9430182adb8da94a25a6039e3859b5d3920e316e12def9f1b563a7819c5f005c82c4a9567a258c
7
+ data.tar.gz: c6fee4da47ba5665bc2e5841d31c0a1fce01ca90d9480a9ac52c8e7d6bca4aef883d6a2cfa93e85b5dcf512466833b70317d7a3b558bd1f894bb09640fd3c4bb
@@ -0,0 +1,55 @@
1
+ # OS X
2
+ .DS_Store
3
+ .AppleDouble
4
+ .LSOverride
5
+ Icon
6
+
7
+ # Thumbnails
8
+ ._*
9
+
10
+ # Files that might appear on external disk
11
+ .Spotlight-V100
12
+ .Trashes
13
+
14
+ # Numerous always-ignore extensions
15
+ *.diff
16
+ *.err
17
+ *.orig
18
+ *.rej
19
+ *.vi
20
+
21
+ # OS or Editor folders
22
+ .cache
23
+ .project
24
+ .settings
25
+ .tmproj
26
+ nbproject
27
+ Thumbs.db
28
+
29
+ # Folders to ignore
30
+ .hg
31
+ .svn
32
+ .CVS
33
+ intermediate
34
+ publish
35
+ .bundle
36
+
37
+ # Vim
38
+ *.s[a-w][a-z]
39
+ *.un~
40
+ Session.vim
41
+ .netrwhist
42
+ *~
43
+
44
+ # RubyMine
45
+ .idea/
46
+
47
+ # Ruby
48
+ log/
49
+ coverage/
50
+
51
+ # jmeter
52
+ *.jt
53
+
54
+ # else
55
+ *.log
@@ -0,0 +1 @@
1
+ exceptional_synchrony
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # ruby '2.0.0'
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in attr_default.gemspec
6
+ gemspec
@@ -0,0 +1,128 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ exceptional_synchrony (1.0.1)
5
+ em-http-request
6
+ em-synchrony (~> 1.0.3)
7
+ eventmachine (~> 1.0.3)
8
+ exception_handling
9
+ hobo_support
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ actionmailer (3.2.13)
15
+ actionpack (= 3.2.13)
16
+ mail (~> 2.5.3)
17
+ actionpack (3.2.13)
18
+ activemodel (= 3.2.13)
19
+ activesupport (= 3.2.13)
20
+ builder (~> 3.0.0)
21
+ erubis (~> 2.7.0)
22
+ journey (~> 1.0.4)
23
+ rack (~> 1.4.5)
24
+ rack-cache (~> 1.2)
25
+ rack-test (~> 0.6.1)
26
+ sprockets (~> 2.2.1)
27
+ activemodel (3.2.13)
28
+ activesupport (= 3.2.13)
29
+ builder (~> 3.0.0)
30
+ activerecord (3.2.13)
31
+ activemodel (= 3.2.13)
32
+ activesupport (= 3.2.13)
33
+ arel (~> 3.0.2)
34
+ tzinfo (~> 0.3.29)
35
+ activeresource (3.2.13)
36
+ activemodel (= 3.2.13)
37
+ activesupport (= 3.2.13)
38
+ activesupport (3.2.13)
39
+ i18n (= 0.6.1)
40
+ multi_json (~> 1.0)
41
+ addressable (2.3.5)
42
+ arel (3.0.3)
43
+ builder (3.0.4)
44
+ cookiejar (0.3.0)
45
+ crack (0.4.1)
46
+ safe_yaml (~> 0.9.0)
47
+ em-http-request (1.1.2)
48
+ addressable (>= 2.3.4)
49
+ cookiejar
50
+ em-socksify (>= 0.3)
51
+ eventmachine (>= 1.0.3)
52
+ http_parser.rb (>= 0.6.0)
53
+ em-socksify (0.3.0)
54
+ eventmachine (>= 1.0.0.beta.4)
55
+ em-synchrony (1.0.3)
56
+ eventmachine (>= 1.0.0.beta.1)
57
+ erubis (2.7.0)
58
+ eventmachine (1.0.3)
59
+ exception_handling (0.1.2)
60
+ actionmailer (= 3.2.13)
61
+ actionpack (= 3.2.13)
62
+ activesupport (= 3.2.13)
63
+ eventmachine (>= 0.12.10)
64
+ hike (1.2.3)
65
+ hobo_support (2.0.1)
66
+ rails (~> 3.2.0)
67
+ http_parser.rb (0.6.0)
68
+ i18n (0.6.1)
69
+ journey (1.0.4)
70
+ json (1.8.1)
71
+ mail (2.5.4)
72
+ mime-types (~> 1.16)
73
+ treetop (~> 1.4.8)
74
+ mime-types (1.25.1)
75
+ minitest (4.7.5)
76
+ multi_json (1.8.4)
77
+ polyglot (0.3.3)
78
+ rack (1.4.5)
79
+ rack-cache (1.2)
80
+ rack (>= 0.4)
81
+ rack-ssl (1.3.3)
82
+ rack
83
+ rack-test (0.6.2)
84
+ rack (>= 1.0)
85
+ rails (3.2.13)
86
+ actionmailer (= 3.2.13)
87
+ actionpack (= 3.2.13)
88
+ activerecord (= 3.2.13)
89
+ activeresource (= 3.2.13)
90
+ activesupport (= 3.2.13)
91
+ bundler (~> 1.0)
92
+ railties (= 3.2.13)
93
+ railties (3.2.13)
94
+ actionpack (= 3.2.13)
95
+ activesupport (= 3.2.13)
96
+ rack-ssl (~> 1.3.2)
97
+ rake (>= 0.8.7)
98
+ rdoc (~> 3.4)
99
+ thor (>= 0.14.6, < 2.0)
100
+ rake (10.1.1)
101
+ rdoc (3.12.2)
102
+ json (~> 1.4)
103
+ rr (1.1.2)
104
+ safe_yaml (0.9.7)
105
+ sprockets (2.2.2)
106
+ hike (~> 1.2)
107
+ multi_json (~> 1.0)
108
+ rack (~> 1.0)
109
+ tilt (~> 1.1, != 1.3.0)
110
+ thor (0.18.1)
111
+ tilt (1.4.1)
112
+ treetop (1.4.15)
113
+ polyglot
114
+ polyglot (>= 0.3.1)
115
+ tzinfo (0.3.38)
116
+ webmock (1.17.1)
117
+ addressable (>= 2.2.7)
118
+ crack (>= 0.3.2)
119
+
120
+ PLATFORMS
121
+ ruby
122
+
123
+ DEPENDENCIES
124
+ exceptional_synchrony!
125
+ minitest
126
+ rr (~> 1.1.2)
127
+ thor
128
+ webmock (~> 1.17.1)
@@ -0,0 +1,4 @@
1
+ exceptional_synchrony
2
+ =====================
3
+
4
+ Extensions to EventMachine/Synchrony to work well with exceptions
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,42 @@
1
+ require 'thor'
2
+
3
+ $LOAD_PATH.unshift('lib')
4
+
5
+ class Dev < Thor
6
+ package_name 'dev'
7
+
8
+ desc 'test' , 'Run the entire test suite or a single file.'
9
+ method_options :file => :string, :n => :string
10
+ def test
11
+
12
+ ENV['RACK_ENV'] = 'test'
13
+
14
+ require 'bundler'
15
+ Bundler.setup(:default, :development)
16
+
17
+ require './test/test_helper.rb'
18
+
19
+ # Minitest is very bad and piggy backs on the Global arguments just like everyone else.
20
+ # Let's stop it from trying to parse Thor's arguments.
21
+ clear_args
22
+
23
+ add_args('-n', options[:n]) if options[:n]
24
+
25
+ # If we have a single file run it, otherwise run them all.
26
+ if single_file = options[:file]
27
+ require single_file
28
+ else
29
+ Dir.glob('./test/**/*_test.rb') { |f| require f }
30
+ end
31
+ end
32
+
33
+ private
34
+ def clear_args
35
+ ARGV.shift(ARGV.length)
36
+ end
37
+
38
+ def add_args(*args)
39
+ args.each { |arg| ARGV << arg }
40
+ end
41
+ end
42
+
@@ -0,0 +1,26 @@
1
+ require File.expand_path('../lib/exceptional_synchrony/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'exceptional_synchrony'
5
+ gem.date = '2014-01-16'
6
+ gem.summary = 'Extensions to EventMachine/Synchrony to work well with exceptions'
7
+ gem.description = %q{}
8
+ gem.authors = ['Colin Kelley']
9
+ gem.email = 'colin@invoca.com'
10
+ gem.homepage = 'https://github.com/Invoca/exceptional_synchrony'
11
+ gem.license = 'MIT'
12
+ gem.files = `git ls-files`.split($/)
13
+ gem.version = ExceptionalSynchrony::VERSION
14
+
15
+ gem.add_dependency 'exception_handling'
16
+ gem.add_dependency 'eventmachine', '~> 1.0.3'
17
+ gem.add_dependency 'em-synchrony', '~> 1.0.3'
18
+ gem.add_dependency 'em-http-request'
19
+ gem.add_dependency 'hobo_support'
20
+
21
+ gem.add_development_dependency 'thor'
22
+ gem.add_development_dependency 'minitest'
23
+ gem.add_development_dependency 'webmock', '~> 1.17.1'
24
+
25
+ gem.add_development_dependency 'rr', '~> 1.1.2'
26
+ end
@@ -0,0 +1,14 @@
1
+ require 'hobo_support'
2
+ require 'em-synchrony'
3
+ require 'em-synchrony/em-http'
4
+ require 'exception_handling'
5
+
6
+
7
+ module ExceptionalSynchrony
8
+ end
9
+
10
+ require_relative 'exceptional_synchrony/callback_exceptions'
11
+ require_relative 'exceptional_synchrony/event_machine_proxy'
12
+ require_relative 'exceptional_synchrony/limited_work_queue'
13
+ require_relative 'exceptional_synchrony/parallel_sync'
14
+ require_relative 'exceptional_synchrony/version'
@@ -0,0 +1,43 @@
1
+ module ExceptionalSynchrony
2
+ module CallbackExceptions
3
+ class Failure < StandardError; end
4
+
5
+ class << self
6
+ def ensure_callback(deferrable, &block)
7
+ result = return_exception(&block)
8
+ deferrable.succeed(*Array(result))
9
+ end
10
+
11
+ def map_deferred_result(deferrable)
12
+ deferred_status = deferrable.instance_variable_get(:@deferred_status)
13
+ deferred_args = deferrable.instance_variable_get(:@deferred_args)
14
+ result = (deferred_args && deferred_args.size == 1) ? deferred_args.first : deferred_args
15
+
16
+ case deferred_status
17
+ when :succeeded
18
+ if result.is_a?(Exception)
19
+ raise result
20
+ else
21
+ result
22
+ end
23
+ when :failed
24
+ if result.respond_to?(:error) && result.error =~ /timeout/i
25
+ raise Timeout::Error
26
+ else
27
+ raise Failure, result.inspect
28
+ end
29
+ else
30
+ raise ArgumentError, "No deferred status set yet: #{deferred_status.inspect} #{result.inspect}"
31
+ end
32
+ end
33
+
34
+ def return_exception(*args)
35
+ begin
36
+ yield *args
37
+ rescue Exception => ex
38
+ ex
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,96 @@
1
+ # This class is for Dependency Injection of EventMachine. All EventMachine interactions should go through here.
2
+
3
+ require 'eventmachine'
4
+ require 'em-http'
5
+ require 'em-synchrony/em-http'
6
+
7
+ module ExceptionalSynchrony
8
+ class EventMachineProxy
9
+
10
+ attr_reader :connection
11
+
12
+ WRAP_WITH_ENSURE_COMPLETELY_SAFE = (ENV['RACK_ENV'] != 'test')
13
+
14
+ def initialize(proxy_class, connection_class)
15
+ @proxy_class = proxy_class
16
+ @synchrony = defined?(@proxy_class::Synchrony) ? @proxy_class::Synchrony : @proxy_class
17
+ @connection = connection_class
18
+
19
+ proxy_class.error_handler do |error|
20
+ ExceptionHandling.log_error(error, "ExceptionalSynchrony uncaught exception: ")
21
+ end
22
+ end
23
+
24
+ def add_timer(seconds, &block)
25
+ @synchrony.add_timer(seconds) do
26
+ ensure_completely_safe("add_timer") do
27
+ block.call
28
+ end
29
+ end
30
+ end
31
+
32
+ def add_periodic_timer(*args, &block)
33
+ @synchrony.add_periodic_timer(*args) do
34
+ ensure_completely_safe("add_periodic_timer") do
35
+ block.call
36
+ end
37
+ end
38
+ end
39
+
40
+ def sleep(seconds)
41
+ @synchrony.sleep(seconds)
42
+ end
43
+
44
+ def next_tick(&block)
45
+ @synchrony.next_tick do
46
+ ensure_completely_safe("next_tick") do
47
+ block.call
48
+ end
49
+ end
50
+ end
51
+
52
+ def stop
53
+ @proxy_class.stop
54
+ @proxy_class.next_tick { } #Fake out EventMachine's epoll mechanism so we don't block until timers fire
55
+ end
56
+
57
+ def connect(server, port = nil, handler = nil, *args, &block)
58
+ @proxy_class.connect(server, port, handler, *args, &block)
59
+ end
60
+
61
+ def run(&block)
62
+ ensure_completely_safe("run") do
63
+ if @proxy_class.respond_to?(:synchrony)
64
+ @proxy_class.synchrony(&block)
65
+ else
66
+ @proxy_class.run(&block)
67
+ end
68
+ end
69
+ end
70
+
71
+ def reactor_running?
72
+ @proxy_class.reactor_running?
73
+ end
74
+
75
+ def run_and_stop
76
+ ret = nil
77
+ run do
78
+ ret = yield
79
+ stop
80
+ end
81
+ ret
82
+ end
83
+
84
+ def ensure_completely_safe(message)
85
+ if WRAP_WITH_ENSURE_COMPLETELY_SAFE
86
+ ExceptionHandling.ensure_completely_safe(message) do
87
+ yield
88
+ end
89
+ else
90
+ yield
91
+ end
92
+ end
93
+ end
94
+
95
+ EMP = EventMachineProxy.new(EventMachine, EventMachine::HttpRequest)
96
+ end
@@ -0,0 +1,52 @@
1
+ module ExceptionalSynchrony
2
+ class LimitedWorkQueue
3
+ def initialize(em, limit)
4
+ @em = em
5
+ limit > 0 or raise ArgumentError, "limit must be positive"
6
+ @limit = limit
7
+ @worker_count = 0
8
+ @job_procs = []
9
+ end
10
+
11
+ # Adds a job_proc to work.
12
+ def add(proc = nil, &block)
13
+ job = proc || block
14
+ job.respond_to?(:call) or raise "Must respond_to?(:call)! #{job.inspect}"
15
+ if @job_procs.any? && job.respond_to?(:merge) && (merged_queue = job.merge(@job_procs))
16
+ @job_procs = merged_queue
17
+ else
18
+ @job_procs << job
19
+ end
20
+ work!
21
+ end
22
+
23
+ def workers_empty?
24
+ @worker_count.zero?
25
+ end
26
+
27
+ def workers_full?
28
+ @worker_count >= @limit
29
+ end
30
+
31
+ def queue_empty?
32
+ @job_procs.empty?
33
+ end
34
+
35
+ private
36
+ def work!
37
+ until queue_empty? || workers_full?
38
+ job_proc = @job_procs.shift
39
+ @worker_count += 1
40
+ Fiber.new do
41
+ job_proc.call
42
+ worker_done
43
+ end.resume
44
+ end
45
+ end
46
+
47
+ def worker_done
48
+ @worker_count -= 1
49
+ work!
50
+ end
51
+ end
52
+ end