torkify 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +230 -0
- data/Rakefile +6 -0
- data/VERSION +1 -0
- data/lib/torkify/conductor.rb +33 -0
- data/lib/torkify/event_parser.rb +36 -0
- data/lib/torkify/events/event.rb +20 -0
- data/lib/torkify/events/event_message.rb +7 -0
- data/lib/torkify/events/pass_or_fail_event.rb +25 -0
- data/lib/torkify/events/status_change_event.rb +20 -0
- data/lib/torkify/events/test_event.rb +15 -0
- data/lib/torkify/exceptions.rb +5 -0
- data/lib/torkify/listener.rb +91 -0
- data/lib/torkify/observer_set.rb +62 -0
- data/lib/torkify/reader.rb +35 -0
- data/lib/torkify/version.rb +3 -0
- data/lib/torkify.rb +40 -0
- data/spec/conductor_spec.rb +63 -0
- data/spec/event_parser_spec.rb +154 -0
- data/spec/event_spec.rb +18 -0
- data/spec/listener_spec.rb +50 -0
- data/spec/observer_set_spec.rb +100 -0
- data/spec/pass_or_fail_event_spec.rb +94 -0
- data/spec/reader_spec.rb +58 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/status_change_event_spec.rb +42 -0
- data/spec/test_event_spec.rb +32 -0
- data/torkify.gemspec +26 -0
- metadata +168 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Jon Cairns
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
# Torkify
|
2
|
+
|
3
|
+
Torkify integrates with [tork][1], which is a solution for automating tests as you change your source files.
|
4
|
+
|
5
|
+
Torkify hooks in to tork's remote events, and allows you to write ruby code that's called when particular events fire. This makes it easy for you to write code that triggers cool stuff when your tests pass or fail.
|
6
|
+
|
7
|
+
## An example
|
8
|
+
|
9
|
+
You define callbacks by creating an observer that defines certain methods. Here's an example that creates a system notification:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
# my_tork_notifier.rb
|
13
|
+
require 'torkify'
|
14
|
+
|
15
|
+
class SystemNotifier
|
16
|
+
def notify(text)
|
17
|
+
# Do a system call to fire a popup notification, e.g. `notify-send`
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_pass(event)
|
21
|
+
notify "Test passed: #{event.file}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_fail(event)
|
25
|
+
notify "Test failed: #{event.file}, log file #{event.log_file}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
listener = Torkify.listener
|
30
|
+
listener.add_observer SystemNotifier.new
|
31
|
+
listener.start
|
32
|
+
```
|
33
|
+
|
34
|
+
This connects to an existing tork process that's running in the current directory.
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
Create a ruby script, load torkify and set up a new listener:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
# my_tork_notifier.rb
|
42
|
+
|
43
|
+
require 'torkify'
|
44
|
+
|
45
|
+
listener = Torkify.listener
|
46
|
+
```
|
47
|
+
|
48
|
+
This listener allows you to add observer objects which you create yourself:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
# my_tork_notifier.rb
|
52
|
+
|
53
|
+
listener.add_observer MyObserver.new
|
54
|
+
listener.start # connect to tork and pass all events to the observer(s)
|
55
|
+
```
|
56
|
+
|
57
|
+
### Observer callback methods
|
58
|
+
|
59
|
+
Your observer classes can define any number of the following methods:
|
60
|
+
|
61
|
+
* `on_startup`: when torkify starts
|
62
|
+
* `on_shutdown`: when torkify shuts down
|
63
|
+
* `on_test`: when a test is started
|
64
|
+
* `on_pass`: when a test passes
|
65
|
+
* `on_fail`: when a test fails
|
66
|
+
* `on_pass_now_fail`: when a previously passed test fails
|
67
|
+
* `on_fail_now_pass`: when a previously failed test passes
|
68
|
+
* `on_absorb`: when tork re-absorbs the environment
|
69
|
+
|
70
|
+
Each method takes an optional event object as a parameter: this contains all the contextual information about that event. See **callback event objects** below for a complete list of accessible attributes on the events.
|
71
|
+
|
72
|
+
### Options for starting torkify
|
73
|
+
|
74
|
+
Calling `start()` on the listener will try and attach to a running tork process in the same directory as the script is run. This starts the process `tork-remote tork-engine` in the current directory. If you want to change the command that's called (e.g. if bundler is giving you grief), then you can pass that as the first parameter to start:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
# my_tork_notifier.rb
|
78
|
+
|
79
|
+
listener.start 'bundle exec tork-remote tork-engine' # Will use this command instead
|
80
|
+
```
|
81
|
+
|
82
|
+
If tork is running in a different directory, you can pass in a path as the second parameter:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# my_tork_notifier.rb
|
86
|
+
|
87
|
+
listener.start 'tork-remote tork-engine', '/home/user/project'
|
88
|
+
```
|
89
|
+
|
90
|
+
`start()` will assume that tork is running, and will exit if no tork process is found. If you want it to keep looping until tork starts, use `start_loop()`:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
# my_tork_notifier.rb
|
94
|
+
|
95
|
+
listener.start_loop # you can pass the same parameters as with start()
|
96
|
+
```
|
97
|
+
|
98
|
+
### Starting with tork
|
99
|
+
|
100
|
+
You may not want to keep tork and torkify separate - for convenience, torkify allows you to start both at the same time. It forks torkify as a child process and runs tork in the parent, allowing you to interact with tork via STDIN but giving you the callbacks of torkify. Just use `start_with_tork()`:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
# my_tork_notifier.rb
|
104
|
+
|
105
|
+
listener.start_with_tork 'bundle exec tork', 'default:logdir' # Starts both tork and torkify
|
106
|
+
```
|
107
|
+
|
108
|
+
Both parameters are optional. The first is the command to execute tork, and the second is the `$TORK_CONFIGS` environment variable.
|
109
|
+
|
110
|
+
### Multiple observers
|
111
|
+
|
112
|
+
You can add multiple observers to your listener:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
require 'torkify'
|
116
|
+
|
117
|
+
class FirstObserver
|
118
|
+
#...
|
119
|
+
end
|
120
|
+
|
121
|
+
class SecondObserver
|
122
|
+
#...
|
123
|
+
end
|
124
|
+
|
125
|
+
listener.Torkify.listener
|
126
|
+
listener.add_observer FirstObserver.new
|
127
|
+
listener.add_observer SecondObserver.new
|
128
|
+
listener.start
|
129
|
+
```
|
130
|
+
|
131
|
+
### Callback event objects
|
132
|
+
|
133
|
+
Here's an example observer, and the accessible data on the events provided in the callbacks:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
|
137
|
+
class MyObserver
|
138
|
+
def on_test(event)
|
139
|
+
event.type #=> "test"
|
140
|
+
event.file #=> "spec/example_spec.rb"
|
141
|
+
event.log_file #=> "spec/example_spec.rb.log"
|
142
|
+
event.lines #=> [10, 11, 12]
|
143
|
+
event.worker #=> 0
|
144
|
+
end
|
145
|
+
|
146
|
+
def on_pass(event)
|
147
|
+
event.type #=> "pass"
|
148
|
+
event.file #=> "spec/example_spec.rb"
|
149
|
+
event.log_file #=> "spec/example_spec.rb.log"
|
150
|
+
event.lines #=> [10, 11, 12]
|
151
|
+
event.worker #=> 0
|
152
|
+
event.exit_code #=> 0
|
153
|
+
event.pid #=> 22813
|
154
|
+
end
|
155
|
+
|
156
|
+
def on_fail(event)
|
157
|
+
event.type #=> "fail"
|
158
|
+
event.file #=> "spec/example_spec.rb"
|
159
|
+
event.log_file #=> "spec/example_spec.rb.log"
|
160
|
+
event.lines #=> [10, 11, 12]
|
161
|
+
event.worker #=> 0
|
162
|
+
event.exit_code #=> 0
|
163
|
+
event.pid #=> 22813
|
164
|
+
end
|
165
|
+
|
166
|
+
# The event has an inner event, which is the same type of event
|
167
|
+
# that's passed to #on_fail()
|
168
|
+
def on_pass_now_fail(event)
|
169
|
+
event.type #=> "pass_now_fail"
|
170
|
+
event.file #=> "spec/example_spec.rb"
|
171
|
+
event.event.type #=> "fail"
|
172
|
+
end
|
173
|
+
|
174
|
+
# The event has an inner event, which is the same type of event
|
175
|
+
# that's passed to #on_pass()
|
176
|
+
def on_fail_now_pass(event)
|
177
|
+
event.type #=> "fail_now_pass"
|
178
|
+
event.file #=> "spec/example_spec.rb"
|
179
|
+
event.event.type #=> "pass"
|
180
|
+
end
|
181
|
+
|
182
|
+
def on_absorb(event)
|
183
|
+
event.type #=> "absorb"
|
184
|
+
end
|
185
|
+
|
186
|
+
def on_startup(event)
|
187
|
+
event.type #=> "startup"
|
188
|
+
end
|
189
|
+
|
190
|
+
def on_shutdown(event)
|
191
|
+
event.type #=> "shutdown"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
## Installation
|
197
|
+
|
198
|
+
Add this line to your application's Gemfile:
|
199
|
+
|
200
|
+
gem 'torkify'
|
201
|
+
|
202
|
+
And then execute:
|
203
|
+
|
204
|
+
$ bundle
|
205
|
+
|
206
|
+
Or install it yourself as:
|
207
|
+
|
208
|
+
$ gem install torkify
|
209
|
+
|
210
|
+
## Usage
|
211
|
+
|
212
|
+
TODO: Write usage instructions here
|
213
|
+
|
214
|
+
## Contributing
|
215
|
+
|
216
|
+
The guidelines are:
|
217
|
+
|
218
|
+
* The tests must pass (run python vdebugtests.py in the top directory of the plugin)
|
219
|
+
* Your commit messages should follow the rules outlined [here][2]
|
220
|
+
|
221
|
+
The steps are:
|
222
|
+
|
223
|
+
1. Fork it
|
224
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
225
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
226
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
227
|
+
5. Create new Pull Request
|
228
|
+
|
229
|
+
[1]: https://github.com/sunaku/tork
|
230
|
+
[2]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative 'events/event'
|
2
|
+
require_relative 'event_parser'
|
3
|
+
|
4
|
+
module Torkify
|
5
|
+
|
6
|
+
# Connect the socket reader and observers, and dispatch events.
|
7
|
+
class Conductor
|
8
|
+
attr_accessor :observers
|
9
|
+
|
10
|
+
# Create with a set of observers.
|
11
|
+
def initialize(observers)
|
12
|
+
@observers = observers
|
13
|
+
end
|
14
|
+
|
15
|
+
# Start reading from the reader, which is an IO-like object.
|
16
|
+
#
|
17
|
+
# Parse each line and dispatch it as an event object to all observers.
|
18
|
+
def start(reader)
|
19
|
+
dispatch Event.new 'startup'
|
20
|
+
parser = EventParser.new
|
21
|
+
reader.each_line do |line|
|
22
|
+
event = parser.parse line
|
23
|
+
dispatch event
|
24
|
+
end
|
25
|
+
dispatch Event.new 'shutdown'
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
def dispatch(event)
|
30
|
+
@observers.dispatch(event)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative 'events/event'
|
3
|
+
require_relative 'events/test_event'
|
4
|
+
require_relative 'events/pass_or_fail_event'
|
5
|
+
require_relative 'events/status_change_event'
|
6
|
+
|
7
|
+
module Torkify
|
8
|
+
|
9
|
+
# Parse raw strings passed by tork into event objects.
|
10
|
+
class EventParser
|
11
|
+
|
12
|
+
# Parse a raw string and return an object based on the event type.
|
13
|
+
#
|
14
|
+
# E.g. a raw string like:
|
15
|
+
# > '["test","spec/reader_spec.rb",[],"spec/reader_spec.rb.log",3]'
|
16
|
+
def parse(line)
|
17
|
+
raw = JSON.load line
|
18
|
+
event_from_data raw
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
# Create an event object from the array of data.
|
23
|
+
def event_from_data(data)
|
24
|
+
case data.first
|
25
|
+
when 'test'
|
26
|
+
TestEvent.new(*data)
|
27
|
+
when /^(pass|fail)$/
|
28
|
+
PassOrFailEvent.new(*data)
|
29
|
+
when /^(pass_now_fail|fail_now_pass)$/
|
30
|
+
StatusChangeEvent.new(data[0], data[1], event_from_data(data[2]))
|
31
|
+
else
|
32
|
+
Event.new(*data)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'event_message'
|
2
|
+
|
3
|
+
module Torkify
|
4
|
+
|
5
|
+
# Event used for all events that have no associated data.
|
6
|
+
#
|
7
|
+
# Types:
|
8
|
+
#
|
9
|
+
# - absorb
|
10
|
+
# - shutdown
|
11
|
+
# - startup
|
12
|
+
# - anything else...
|
13
|
+
class Event < Struct.new(:type)
|
14
|
+
include EventMessage
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
type
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'event_message'
|
2
|
+
|
3
|
+
module Torkify
|
4
|
+
|
5
|
+
# Event used for test passes or failures.
|
6
|
+
#
|
7
|
+
# Types:
|
8
|
+
#
|
9
|
+
# - pass
|
10
|
+
# - fail
|
11
|
+
class PassOrFailEvent < Struct.new(:type, :file, :lines, :log_file, :worker, :exit_code, :exit_info)
|
12
|
+
include EventMessage
|
13
|
+
|
14
|
+
# Get the PID from the exit info.
|
15
|
+
def pid
|
16
|
+
matched = exit_info.scan(/pid ([0-9]+)/).first
|
17
|
+
matched.first.to_i if matched
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
s = "#{type.upcase} #{file}"
|
22
|
+
s += lines.any? ? " (lines #{lines.join(', ')})" : ''
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'event_message'
|
2
|
+
|
3
|
+
module Torkify
|
4
|
+
|
5
|
+
# Event used for changes in test status.
|
6
|
+
#
|
7
|
+
# Types:
|
8
|
+
#
|
9
|
+
# - pass_now_fail
|
10
|
+
# - fail_now_pass
|
11
|
+
#
|
12
|
+
# Includes the actual fail/pass event as a separate object.
|
13
|
+
class StatusChangeEvent < Struct.new(:type, :file, :event)
|
14
|
+
include EventMessage
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"#{type.upcase.gsub('_',' ')} #{file}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'event_message'
|
2
|
+
|
3
|
+
module Torkify
|
4
|
+
# Event used when a test is started.
|
5
|
+
#
|
6
|
+
# This is currently only one type: 'test'
|
7
|
+
class TestEvent < Struct.new(:type, :file, :lines, :log_file, :worker)
|
8
|
+
include EventMessage
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
s = "#{type.upcase} #{file}"
|
12
|
+
s += lines.any? ? " (lines #{lines.join(', ')})" : ''
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative 'conductor'
|
2
|
+
require_relative 'observer_set'
|
3
|
+
require_relative 'reader'
|
4
|
+
require_relative 'exceptions'
|
5
|
+
|
6
|
+
module Torkify
|
7
|
+
class Listener
|
8
|
+
# Create a torkify listener with optional command and directory specified.
|
9
|
+
#
|
10
|
+
# The command is what's used to start the tork remote engine. The
|
11
|
+
# directory is where the command will be executed.
|
12
|
+
def initialize(command = 'tork-remote tork-engine', dir = Dir.pwd)
|
13
|
+
@command = command
|
14
|
+
@dir = dir
|
15
|
+
@conductor = Conductor.new ObserverSet.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Add an observer object to be notified of tork events.
|
19
|
+
#
|
20
|
+
# The object will be notified of events if it contains the following
|
21
|
+
# methods:
|
22
|
+
#
|
23
|
+
# - on_startup(event) - when torkify starts
|
24
|
+
# - on_shutdown(event) - when torkify shuts down
|
25
|
+
# - on_test(event) - when a test is started
|
26
|
+
# - on_pass(event) - when a test passes
|
27
|
+
# - on_fail(event) - when a test fails
|
28
|
+
# - on_fail_now_pass(event) - when a previously failed test passes
|
29
|
+
# - on_pass_now_fail(event) - when a previously passed test fails
|
30
|
+
# - on_absorb(event) - when tork reabsorbs overhead
|
31
|
+
#
|
32
|
+
# It doesn't have to inherit from a particular type, just define the
|
33
|
+
# method. The argument is optional, and in each case it gives an event.
|
34
|
+
def add_observer(observer)
|
35
|
+
@conductor.observers << observer
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
# Start the torkify listener and dispatch events to observers.
|
40
|
+
#
|
41
|
+
# This runs once, and connects to an existing tork process. If tork
|
42
|
+
# stops running then torkify will shut down.
|
43
|
+
#
|
44
|
+
# For continuous listening use #start_loop() instead.
|
45
|
+
#
|
46
|
+
# To start tork as well, use #start_with_tork().
|
47
|
+
def start
|
48
|
+
reader = Reader.new(@command, @dir)
|
49
|
+
Torkify.logger.info { 'Started torkify listener' }
|
50
|
+
@conductor.start reader
|
51
|
+
Torkify.logger.info { 'Stopping torkify' }
|
52
|
+
self
|
53
|
+
rescue Torkify::TorkError
|
54
|
+
Torkify.logger.info { "Tork is not running" }
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
# Start the torkify listener and loop until it connects to a tork process.
|
59
|
+
#
|
60
|
+
# If the tork process stops, it will keep looping until another starts.
|
61
|
+
#
|
62
|
+
# Calls #start().
|
63
|
+
def start_loop
|
64
|
+
loop do
|
65
|
+
sleep 2
|
66
|
+
start
|
67
|
+
end
|
68
|
+
rescue => e
|
69
|
+
Torkify.logger.error { e }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Start the torkify listener and tork itself.
|
73
|
+
#
|
74
|
+
# It forks the current process and runs torkify as the child. It then
|
75
|
+
# runs tork as the main process, allowing for stdin to be passed to tork.
|
76
|
+
#
|
77
|
+
# Calls #start_loop().
|
78
|
+
#
|
79
|
+
# The command to run tork can be passed as an argument, and the
|
80
|
+
# TORK_CONFIGS environment variable can be passed as the second argument.
|
81
|
+
def start_with_tork(command = 'tork', tork_env = 'default')
|
82
|
+
if fork
|
83
|
+
start_loop
|
84
|
+
else
|
85
|
+
# Run tork in main process to keep stdin
|
86
|
+
Torkify.logger.info { "Starting tork" }
|
87
|
+
exec({'TORK_CONFIGS' => tork_env}, command)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Torkify
|
4
|
+
# Wrapper around a Set class, that adds a dispatch method.
|
5
|
+
#
|
6
|
+
# Dispatch sends an event that calls a method on all observers.
|
7
|
+
class ObserverSet
|
8
|
+
def initialize(set = Set.new)
|
9
|
+
@set = set
|
10
|
+
end
|
11
|
+
|
12
|
+
# Call a method on all observers, depending on the event type.
|
13
|
+
#
|
14
|
+
# The method is the event type prefixed with "on_". E.g. 'test' would be
|
15
|
+
# 'on_test'.
|
16
|
+
def dispatch(event)
|
17
|
+
Torkify.logger.debug event.to_s
|
18
|
+
@set.each do |observer|
|
19
|
+
dispatch_each observer, event.message, event
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Don't return a Set, return an ObserverSet.
|
24
|
+
def |(enum)
|
25
|
+
self.class.new(@set | enum)
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(method, *args, &blk)
|
29
|
+
@set.send method, *args, &blk
|
30
|
+
end
|
31
|
+
|
32
|
+
def respond_to?(name, include_private = false)
|
33
|
+
@set.respond_to? name, include_private
|
34
|
+
end
|
35
|
+
|
36
|
+
alias :+ :|
|
37
|
+
alias :union :|
|
38
|
+
|
39
|
+
private
|
40
|
+
# Send the messages to a given observer, with the event object.
|
41
|
+
def dispatch_each(observer, message, event)
|
42
|
+
method = observer.method(message)
|
43
|
+
observer.send message, *method_args(method, event)
|
44
|
+
rescue NameError
|
45
|
+
Torkify.logger.warn { "No method #{message} defined on #{observer.inspect}" }
|
46
|
+
rescue => e
|
47
|
+
Torkify.logger.error { "Caught exception from #{observer} during ##{message}: #{e}" }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Determine whether to include the event in the arguments.
|
51
|
+
#
|
52
|
+
# The arity of the obsever's method is checked to see whether an
|
53
|
+
# argument is received or not.
|
54
|
+
def method_args(method, event)
|
55
|
+
dispatch_args = []
|
56
|
+
unless method.arity === 0
|
57
|
+
dispatch_args << event
|
58
|
+
end
|
59
|
+
dispatch_args
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require_relative 'exceptions'
|
3
|
+
|
4
|
+
module Torkify
|
5
|
+
class Reader
|
6
|
+
# Open the tork command and initialize the streams.
|
7
|
+
#
|
8
|
+
# STDOUT is kept as the underlying stream, and this class can be used as
|
9
|
+
# an IO-like object on STDOUT.
|
10
|
+
#
|
11
|
+
# A TorkError is raised if the command fails, and its message is whatever
|
12
|
+
# has been written to the command's STDERR stream.
|
13
|
+
def initialize(command = 'tork-remote tork-engine', run_in_dir = Dir.pwd)
|
14
|
+
Dir.chdir(run_in_dir) do
|
15
|
+
_, @io, stderr, _ = Open3.popen3 command
|
16
|
+
|
17
|
+
if @io.eof?
|
18
|
+
raise TorkError, stderr.read.strip
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Pass all unknown methods straight to the underlying IO object.
|
24
|
+
#
|
25
|
+
# This allows this class to be used in an IO like way.
|
26
|
+
def method_missing(method, *args, &blk)
|
27
|
+
@io.send method, *args, &blk
|
28
|
+
end
|
29
|
+
|
30
|
+
# Allow respond_to? to work with method_missing.
|
31
|
+
def respond_to?(method, include_private = false)
|
32
|
+
@io.respond_to? method, include_private
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/torkify.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "torkify/version"
|
2
|
+
|
3
|
+
# Listen to tork events and execute ruby code when they happen.
|
4
|
+
#
|
5
|
+
# E.g.
|
6
|
+
#
|
7
|
+
# listener = Torkify.listener
|
8
|
+
# class Observer
|
9
|
+
# def on_pass(event)
|
10
|
+
# puts event.to_s
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
# listener.add_observer Observer.new
|
14
|
+
# listener.start
|
15
|
+
# # or listener.start_loop
|
16
|
+
# # or listener.start_with_tork
|
17
|
+
module Torkify
|
18
|
+
|
19
|
+
# Create a listener object and load all required files.
|
20
|
+
def self.listener(*args)
|
21
|
+
require 'torkify/listener'
|
22
|
+
Listener.new(*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create a logger object, or retrieve the existing logger.
|
26
|
+
#
|
27
|
+
# Uses Log4r.
|
28
|
+
def self.logger
|
29
|
+
require 'log4r'
|
30
|
+
include Log4r
|
31
|
+
|
32
|
+
log = Logger['torkify']
|
33
|
+
unless log
|
34
|
+
log = Logger.new 'torkify'
|
35
|
+
log.outputters = Outputter.stdout
|
36
|
+
log.level = INFO
|
37
|
+
end
|
38
|
+
log
|
39
|
+
end
|
40
|
+
end
|