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.
@@ -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
- Logger.error :live, "Couldn't connect to notification server at #{host}:#{port}"
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
- Logger.error :live, "Lost notification server connection"
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.start
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
- @subscribers = Set.new
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.delegate_gc
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
- Logger.log :live, "received notif #{notif.inspect}" if $verbose
91
-
92
- delegate_gc
93
-
94
- @subscribers.each do |sub|
95
- blk = sub[:block]
96
- if wants_notification(sub, notif)
97
- blk.arity == 1 ? blk.call(notif) : blk.call
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
- # TODO: 2 secs right here?
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, sig, &blk)
118
- @subscribers << {:sig => sig, :block => blk, :delegate => obj}
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
- $stdout.puts "got #{line}" if $verbose
35
+ logd "got #{line}"
35
36
  NotifChannel << line
36
37
  end
37
38
  end
@@ -1,7 +1,11 @@
1
1
  <html><head>
2
- <title><%= page.canvas.title %></title>
3
- <script src="/jq.js"></script><% if page.class.live_updates_enabled? %><script>Goat.page_id = '<%= page.id %>'</script><% end %>
2
+ <title><%= canvas.title %></title>
3
+ <style><%= canvas.style %></style>
4
4
  </head>
5
5
  <body>
6
6
  <%= yield %>
7
- </body></html>
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: 15
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 12
10
- version: 0.2.12
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-10 00:00:00 +00:00
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/logger.rb
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/sinatra.rb
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
data/lib/goat/sinatra.rb DELETED
@@ -1,11 +0,0 @@
1
- class HTMLString < String; end
2
-
3
- def h!(str)
4
- HTMLString.new(str)
5
- end
6
-
7
- class String
8
- def h!
9
- HTMLString.new(self)
10
- end
11
- end