goat 0.2.12 → 0.3.0
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.
- data/README.md +45 -0
- data/bin/channel-srv +150 -0
- data/bin/press +29 -0
- data/bin/state-srv +390 -0
- data/bin/sync +53 -0
- data/bin/yodel +1 -1
- data/goat.gemspec +37 -0
- data/lib/goat.rb +774 -618
- data/lib/goat/common.rb +53 -0
- data/lib/goat/dynamic.rb +91 -0
- data/lib/goat/extn.rb +94 -5
- data/lib/goat/goat.js +348 -119
- data/lib/goat/html.rb +221 -103
- data/lib/goat/js/component.js +18 -15
- data/lib/goat/net-common.rb +38 -0
- data/lib/goat/notifications.rb +51 -46
- data/lib/goat/state-srv.rb +119 -0
- data/lib/goat/yodel.rb +3 -2
- data/lib/views/plain_layout.erb +7 -3
- metadata +18 -10
- data/lib/goat/logger.rb +0 -39
- data/lib/goat/sinatra.rb +0 -11
data/lib/goat/notifications.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'eventmachine'
|
2
2
|
|
3
|
-
require File.join(File.dirname(__FILE__), 'logger')
|
4
|
-
|
5
3
|
module Goat
|
6
4
|
module NotificationCenter
|
7
5
|
class Receiver < EM::Connection
|
@@ -10,7 +8,7 @@ module Goat
|
|
10
8
|
def self.start(host, port)
|
11
9
|
EM.connect(host, port, self)
|
12
10
|
rescue RuntimeError => e
|
13
|
-
|
11
|
+
log :live, "Couldn't connect to notification server at #{host}:#{port}"
|
14
12
|
raise e
|
15
13
|
end
|
16
14
|
|
@@ -19,27 +17,18 @@ module Goat
|
|
19
17
|
end
|
20
18
|
|
21
19
|
def unbind
|
22
|
-
|
20
|
+
logw "Lost notification server connection"
|
23
21
|
EM.add_timer(1) do
|
24
22
|
NotificationCenter.start_receiver
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
28
26
|
|
29
|
-
def self.init
|
30
|
-
unless @running
|
31
|
-
start if @configured
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
27
|
def self.start_receiver
|
36
28
|
Receiver.start(@host, @recv_port)
|
37
29
|
end
|
38
30
|
|
39
|
-
def self.
|
40
|
-
start_receiver
|
41
|
-
@running = true
|
42
|
-
end
|
31
|
+
def self.enabled?; @configured; end
|
43
32
|
|
44
33
|
def self.configure(opts={})
|
45
34
|
opts = {:host => '127.0.0.1', :recv_port => 8000, :send_port => 8001}.merge(opts)
|
@@ -48,9 +37,11 @@ module Goat
|
|
48
37
|
@recv_port = opts[:recv_port]
|
49
38
|
@send_port = opts[:send_port]
|
50
39
|
|
51
|
-
|
40
|
+
EM.next_tick { self.start_receiver }
|
52
41
|
end
|
53
42
|
|
43
|
+
def self.subscribers; @subscribers ||= Set.new; end
|
44
|
+
|
54
45
|
def self.wants_notification(sub, notif)
|
55
46
|
wants = true
|
56
47
|
sub[:sig].each do |k, v|
|
@@ -66,56 +57,70 @@ module Goat
|
|
66
57
|
def self.notif_to_json(notif)
|
67
58
|
notif.to_json
|
68
59
|
end
|
69
|
-
|
70
|
-
def self.
|
71
|
-
kill_list = Set.new
|
72
|
-
Logger.log :gc, "Delegate gc"
|
73
|
-
@subscribers.each do |sub|
|
74
|
-
# $stderr.puts "Testing removal of #{sub.inspect}"
|
75
|
-
dlg = sub[:delegate]
|
76
|
-
# $stderr.puts "Responds to dead? #{dlg.respond_to?(:dead?)}"
|
77
|
-
if dlg.respond_to?(:dead?) && dlg.dead?
|
78
|
-
Logger.log :gc, "Dead"
|
79
|
-
kill_list << sub
|
80
|
-
next
|
81
|
-
end
|
82
|
-
end
|
83
|
-
# $stderr.puts "Removing #{kill_list.inspect}"
|
84
|
-
kill_list.each {|sub| @subscribers.delete(sub)}
|
85
|
-
# $stderr.puts "subscribers now: #{@subscribers.map{|x| x.inspect[0..50]}.inspect}"
|
86
|
-
end
|
60
|
+
|
61
|
+
def self.received_notifications; @recv_notif ||= Set.new; end
|
87
62
|
|
88
63
|
def self.receive(line)
|
89
64
|
notif = load_notif(line)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
65
|
+
nid = notif['_nid']
|
66
|
+
logd "received notif #{notif.inspect}" if $verbose
|
67
|
+
|
68
|
+
raise "No _nid" unless nid
|
69
|
+
|
70
|
+
unless received_notifications.include?(nid)
|
71
|
+
received_notifications << nid
|
72
|
+
process_notification(notif)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.process_notification(notif)
|
77
|
+
if(notif['txn'])
|
78
|
+
bind = {:txn => notif['txn'], :txn_pgid => notif['txn_pgid']}
|
79
|
+
else
|
80
|
+
bind = {}
|
81
|
+
end
|
82
|
+
|
83
|
+
Dynamic.let(bind) do
|
84
|
+
subscribers.each do |sub|
|
85
|
+
if wants_notification(sub, notif)
|
86
|
+
$stderr.puts "Dispatching to #{sub[:delegate]}"
|
87
|
+
meth = sub[:meth]
|
88
|
+
sub[:delegate].send(meth, notif)
|
89
|
+
end
|
98
90
|
end
|
99
91
|
end
|
100
92
|
end
|
101
93
|
|
102
94
|
def self.notify(notif)
|
95
|
+
if Dynamic.variable?(:txn)
|
96
|
+
notif['txn'] = Dynamic[:txn]
|
97
|
+
notif['txn_pgid'] = Dynamic[:txn_pgid]
|
98
|
+
end
|
99
|
+
|
100
|
+
nid = String.random
|
101
|
+
notif['_nid'] = nid
|
102
|
+
|
103
|
+
process_notification(notif) # ensure local delivery happens first
|
104
|
+
received_notifications << nid
|
105
|
+
|
103
106
|
if EM.reactor_running?
|
104
107
|
EM.connect(@host, @send_port) do |c|
|
105
108
|
# TODO: alert if this fails
|
106
109
|
c.send_data(notif_to_json(notif) + "\n")
|
107
|
-
|
108
|
-
EM.add_timer(2) { c.close_connection }
|
110
|
+
c.close_connection(true)
|
109
111
|
end
|
110
112
|
else
|
111
113
|
s = TCPSocket.open(@host, @send_port)
|
112
114
|
s.write(notif_to_json(notif) + "\n")
|
113
115
|
s.close
|
114
116
|
end
|
117
|
+
rescue SocketError => e
|
118
|
+
$stderr.puts "Couldn't notify host #{@host.inspect}:#{@send_port.inspect}"
|
119
|
+
raise e
|
115
120
|
end
|
116
121
|
|
117
|
-
def self.subscribe(obj,
|
118
|
-
|
122
|
+
def self.subscribe(obj, meth, sig)
|
123
|
+
subscribers << {:sig => sig, :meth => meth, :delegate => obj}
|
119
124
|
end
|
120
125
|
end
|
121
|
-
end
|
126
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Goat
|
2
|
+
module StateSrvClient
|
3
|
+
def self.configure(opts={})
|
4
|
+
# TODO implement fully
|
5
|
+
EM.next_tick { StateSrvConnection.connect }
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.send_message(type, m, sync=false)
|
9
|
+
StateSrvConnection.send_message(type, m, sync)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register_page(pgid, cs)
|
13
|
+
send_message('register_page', 'pgid' => pgid, 'components' => cs.map(&:to_hash))
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.live_components(cls, spec)
|
17
|
+
resp = send_message('live_components', {'class' => cls, 'spec' => spec}, true)
|
18
|
+
JSON.load(resp)['response'].map{|h| ComponentSkeleton.from_hash(h)}
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.fetch_component(id)
|
22
|
+
resp = send_message('fetch_component', {'id' => id}, true)
|
23
|
+
ComponentSkeleton.from_hash(JSON.load(resp)['response'])
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.component_updated(txn, pgid, update)
|
27
|
+
components_updated(txn, pgid, [update])
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.components_updated(txn, pgid, updates)
|
31
|
+
send_message('components_updated',
|
32
|
+
'txn' => txn,
|
33
|
+
'pgid' => pgid,
|
34
|
+
'updates' => updates.map(&:to_hash)
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class NoStateSrvConnectionError < RuntimeError; end
|
40
|
+
|
41
|
+
class StateSrvConnection < EM::Connection
|
42
|
+
include EM::P::LineText2
|
43
|
+
|
44
|
+
@@connection = nil
|
45
|
+
def self.connection; @@connection; end
|
46
|
+
def self.connection=(c); @@connection = c; end
|
47
|
+
def self.connected?; @@connection != nil; end
|
48
|
+
|
49
|
+
def self.connect(host='127.0.0.1', port=8011, &dlg)
|
50
|
+
@host = host
|
51
|
+
@port = port
|
52
|
+
EM.connect(host, port, self)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.send_message_sync(msg)
|
56
|
+
s = TCPSocket.open(@host, @port)
|
57
|
+
s.write(msg.to_json + "\n")
|
58
|
+
resp = s.readline
|
59
|
+
logd("=> #{resp.inspect}") if $verbose
|
60
|
+
s.close
|
61
|
+
resp
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.send_message(*args)
|
65
|
+
if self.connected?
|
66
|
+
self.connection.send_message(*args)
|
67
|
+
else
|
68
|
+
raise NoStateSrvConnectionError
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
attr_accessor :pusher
|
73
|
+
|
74
|
+
def connection_completed
|
75
|
+
@was_connected = true
|
76
|
+
logw "Connected to StateSrv"
|
77
|
+
StateSrvConnection.connection = self
|
78
|
+
end
|
79
|
+
|
80
|
+
def receive_line(line)
|
81
|
+
msg = JSON.load(line)
|
82
|
+
|
83
|
+
logd("=> #{msg.inspect}") if $verbose
|
84
|
+
|
85
|
+
if msg.is_a?(Array)
|
86
|
+
msg.each{|m| message_received(m)}
|
87
|
+
else
|
88
|
+
message_received(msg)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def send_message(t, msg, sync=false)
|
93
|
+
msg = msg.merge('type' => t)
|
94
|
+
|
95
|
+
logd(">> #{msg.inspect}") if $verbose
|
96
|
+
|
97
|
+
if sync
|
98
|
+
self.class.send_message_sync(msg) # TODO better way to do this?
|
99
|
+
else
|
100
|
+
send_data(msg.to_json + "\n")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def close
|
105
|
+
close_connection
|
106
|
+
end
|
107
|
+
|
108
|
+
def unbind
|
109
|
+
if @was_connected
|
110
|
+
logw "Lost StateSrv connection"
|
111
|
+
else
|
112
|
+
logw "Couldn't open StateSrv connection"
|
113
|
+
end
|
114
|
+
|
115
|
+
StateSrvConnection.connection = nil
|
116
|
+
EM.add_timer(5) { self.class.connect }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/goat/yodel.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'rubygems'
|
3
2
|
require 'eventmachine'
|
3
|
+
require File.join(File.dirname(__FILE__), 'net-common')
|
4
4
|
|
5
5
|
$verbose = ARGV.include?('-v')
|
6
6
|
|
@@ -13,6 +13,7 @@ module Yodel
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def post_init
|
16
|
+
logw "listener connected"
|
16
17
|
@sub = NotifChannel.subscribe do |msg|
|
17
18
|
send_data(msg + "\n")
|
18
19
|
end
|
@@ -31,7 +32,7 @@ module Yodel
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def receive_line(line)
|
34
|
-
|
35
|
+
logd "got #{line}"
|
35
36
|
NotifChannel << line
|
36
37
|
end
|
37
38
|
end
|
data/lib/views/plain_layout.erb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
<html><head>
|
2
|
-
<title><%=
|
3
|
-
<
|
2
|
+
<title><%= canvas.title %></title>
|
3
|
+
<style><%= canvas.style %></style>
|
4
4
|
</head>
|
5
5
|
<body>
|
6
6
|
<%= yield %>
|
7
|
-
|
7
|
+
<script src="/static/jquery.js"></script>
|
8
|
+
<script src="/static/goat.js"></script>
|
9
|
+
<script><%= canvas.script %></script>
|
10
|
+
<% if page.class.live_updates_enabled? %><script>Goat.page_id = '<%= page.id %>'</script><% end %>
|
11
|
+
</body></html>
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: goat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Patrick Collison
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-11-
|
18
|
+
date: 2010-11-18 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -28,20 +28,28 @@ extensions: []
|
|
28
28
|
extra_rdoc_files: []
|
29
29
|
|
30
30
|
files:
|
31
|
+
- README.md
|
32
|
+
- bin/channel-srv
|
33
|
+
- bin/press
|
34
|
+
- bin/state-srv
|
35
|
+
- bin/sync
|
31
36
|
- bin/yodel
|
37
|
+
- goat.gemspec
|
32
38
|
- lib/goat/autobind.rb
|
39
|
+
- lib/goat/common.rb
|
40
|
+
- lib/goat/dynamic.rb
|
33
41
|
- lib/goat/extn.rb
|
34
42
|
- lib/goat/goat.js
|
35
|
-
- lib/goat.rb
|
36
43
|
- lib/goat/html.rb
|
37
|
-
- lib/goat/
|
44
|
+
- lib/goat/js/component.js
|
38
45
|
- lib/goat/mongo.rb
|
46
|
+
- lib/goat/net-common.rb
|
39
47
|
- lib/goat/notifications.rb
|
40
|
-
- lib/goat/
|
41
|
-
- lib/goat/yodel.rb
|
48
|
+
- lib/goat/state-srv.rb
|
42
49
|
- lib/goat/static.rb
|
50
|
+
- lib/goat/yodel.rb
|
51
|
+
- lib/goat.rb
|
43
52
|
- lib/views/plain_layout.erb
|
44
|
-
- lib/goat/js/component.js
|
45
53
|
has_rdoc: true
|
46
54
|
homepage: http://goatweb.org
|
47
55
|
licenses: []
|
data/lib/goat/logger.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'term/ansicolor'
|
2
|
-
|
3
|
-
module Goat
|
4
|
-
class Logger
|
5
|
-
class << self
|
6
|
-
def init
|
7
|
-
@levels = [:error]
|
8
|
-
@categories = nil
|
9
|
-
end
|
10
|
-
|
11
|
-
def levels=(types)
|
12
|
-
@levels = types
|
13
|
-
end
|
14
|
-
|
15
|
-
def categories=(cats)
|
16
|
-
@categories = cats
|
17
|
-
end
|
18
|
-
|
19
|
-
def error(cat, str)
|
20
|
-
log(cat, str, :error)
|
21
|
-
end
|
22
|
-
|
23
|
-
def log(cat, str, level = :debug)
|
24
|
-
if @categories.kind_of?(Array) && level != :error
|
25
|
-
return unless @categories.include?(cat)
|
26
|
-
end
|
27
|
-
|
28
|
-
return unless @levels.include?(level)
|
29
|
-
|
30
|
-
levelstr = level.to_s.ljust(13)
|
31
|
-
colorlevel = level == :error ? Term::ANSIColor.red(levelstr) : levelstr
|
32
|
-
|
33
|
-
$stderr.puts "#{colorlevel} [#{Time.now.strftime("%d/%b/%Y %H:%M:%S")}] #{Term::ANSIColor.green(str)}"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
init
|
38
|
-
end
|
39
|
-
end
|