gom-script 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.markdown +31 -0
- data/Rakefile +67 -0
- data/VERSION +1 -0
- data/lib/gom-script.rb +1 -0
- data/lib/gom/remote.rb +8 -0
- data/lib/gom/remote/connection.rb +194 -0
- data/lib/gom/remote/daemon.rb +78 -0
- data/lib/gom/remote/entry.rb +27 -0
- data/lib/gom/remote/http_server.rb +124 -0
- data/lib/gom/remote/subscription.rb +38 -0
- data/spec/gom/remote/connection_spec.rb +109 -0
- data/spec/gom/remote/daemon_spec.rb +57 -0
- data/spec/gom/remote/http_server_spec.rb +106 -0
- data/spec/gom/remote/subscription_spec.rb +48 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +48 -0
- metadata +127 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 art+com AG/dirk luesebrink
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# GOM Script
|
2
|
+
|
3
|
+
Easy access over the wire to a remote GOM node. This gem includes a GNP
|
4
|
+
callback server, support for automatic observer refreshments and command line
|
5
|
+
tools for reading, writing and observing GOM entries.
|
6
|
+
|
7
|
+
|
8
|
+
## Install
|
9
|
+
|
10
|
+
use the jewler tasks:
|
11
|
+
|
12
|
+
$ rake gemspec build install
|
13
|
+
|
14
|
+
## Dependencies
|
15
|
+
|
16
|
+
## credits
|
17
|
+
|
18
|
+
## Note on Patches/Pull Requests
|
19
|
+
|
20
|
+
* Fork the project.
|
21
|
+
* Make your feature addition or bug fix.
|
22
|
+
* Add tests for it. This is important so I don't break it in a
|
23
|
+
future version unintentionally.
|
24
|
+
* Commit, do not mess with rakefile, version, or history.
|
25
|
+
(if you want to have your own version, that is fine but
|
26
|
+
bump version in a commit by itself I can ignore when I pull)
|
27
|
+
* Send me a pull request. Bonus points for topic branches.
|
28
|
+
|
29
|
+
## Copyright
|
30
|
+
|
31
|
+
Copyright (c) 2009 art+com AG/dirk luesebrink. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
# gem is a Gem::Specification... see
|
8
|
+
# http://www.rubygems.org/read/chapter/20 for additional settings
|
9
|
+
#
|
10
|
+
gem.name = "gom-script"
|
11
|
+
gem.summary = %Q{connecting scripts and daemons with a remote GOM instance}
|
12
|
+
gem.description = %Q{
|
13
|
+
GOM is a schema-less object database in ruby with Resource Oriented API,
|
14
|
+
server-side javascript, distributed HTTP notifications and some more.
|
15
|
+
This gom-script script simplifies coding of clients and daemon which like
|
16
|
+
to listen on state change event in the GOM.
|
17
|
+
}.gsub /\n\n/, ''
|
18
|
+
gem.email = "dirk.luesebrink@gmail.com"
|
19
|
+
gem.homepage = "http://github.com/crux/gom-script"
|
20
|
+
gem.authors = ["art+com/dirk luesebrink"]
|
21
|
+
gem.add_runtime_dependency "applix", ">=0.2.1"
|
22
|
+
gem.add_runtime_dependency "rack"
|
23
|
+
gem.add_runtime_dependency "gom-core"
|
24
|
+
|
25
|
+
gem.add_development_dependency "rspec"
|
26
|
+
gem.add_development_dependency "fakeweb", ">=1.2.7"
|
27
|
+
end
|
28
|
+
rescue LoadError
|
29
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
30
|
+
end
|
31
|
+
|
32
|
+
require 'spec/rake/spectask'
|
33
|
+
desc "Run all specs in spec directory"
|
34
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
35
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
36
|
+
t.spec_opts = %w(-c)
|
37
|
+
end
|
38
|
+
|
39
|
+
begin
|
40
|
+
require 'rcov/rcovtask'
|
41
|
+
Rcov::RcovTask.new do |test|
|
42
|
+
test.libs << 'test'
|
43
|
+
test.pattern = 'spec/**/*_spec.rb'
|
44
|
+
test.verbose = true
|
45
|
+
end
|
46
|
+
rescue LoadError
|
47
|
+
task :rcov do
|
48
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
require 'rake/rdoctask'
|
53
|
+
Rake::RDocTask.new do |rdoc|
|
54
|
+
if File.exist?('VERSION')
|
55
|
+
version = File.read('VERSION')
|
56
|
+
else
|
57
|
+
version = ""
|
58
|
+
end
|
59
|
+
|
60
|
+
rdoc.rdoc_dir = 'rdoc'
|
61
|
+
rdoc.title = "GOM Remote - #{version}"
|
62
|
+
rdoc.rdoc_files.include('README*')
|
63
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
64
|
+
end
|
65
|
+
|
66
|
+
task :test => :check_dependencies
|
67
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.2
|
data/lib/gom-script.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'gom/remote'
|
data/lib/gom/remote.rb
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Gom
|
6
|
+
module Remote
|
7
|
+
|
8
|
+
class << self; attr_accessor :connection; end
|
9
|
+
|
10
|
+
class Connection
|
11
|
+
|
12
|
+
attr_reader :target_url, :initial_path, :callback_server
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# take apart the URL into GOM and node path part
|
16
|
+
def split_url url
|
17
|
+
u = URI.parse url
|
18
|
+
re = %r|#{u.scheme}://#{u.host}(:#{u.port})?|
|
19
|
+
server = (re.match url).to_s
|
20
|
+
path = (url.sub server, '').sub(/\/$/, '')
|
21
|
+
[server, path]
|
22
|
+
end
|
23
|
+
|
24
|
+
def init url, callback_port = nil
|
25
|
+
connection = (self.new url, callback_port)
|
26
|
+
[connection, connection.initial_path]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# url: initial GOM url, path or attribute. The remote GOM server
|
31
|
+
# address gets extracted from this and, unless nil, the given block
|
32
|
+
# will be called with the remaining GOM path, aka:
|
33
|
+
#
|
34
|
+
# url == http://gom:1234/foo/bar:attribute
|
35
|
+
#
|
36
|
+
# will use 'http://gom:1234' as GOM server and call the block with
|
37
|
+
# '/foo/bar:attribute' as path argument.
|
38
|
+
#
|
39
|
+
def initialize url, callback_port = nil
|
40
|
+
@target_url, @initial_path = (Connection.split_url url)
|
41
|
+
#Gom::Remote.connection and (raise "connection already open")
|
42
|
+
Gom::Remote.connection = self
|
43
|
+
|
44
|
+
@subscriptions = []
|
45
|
+
@callback_server = init_callback_server callback_port
|
46
|
+
end
|
47
|
+
|
48
|
+
def write path, value
|
49
|
+
if value.kind_of? Hash
|
50
|
+
write_node path, attributes
|
51
|
+
else
|
52
|
+
write_attribute path, value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def write_attribute path, value
|
57
|
+
# TODO: Primitive#encode returns to_s for all unknow types. exception
|
58
|
+
# would be correct.
|
59
|
+
txt, type = (Gom::Core::Primitive.encode value)
|
60
|
+
params = { "attribute" => txt, "type" => type }
|
61
|
+
url = "#{@target_url}#{path}"
|
62
|
+
http_put(url, params)
|
63
|
+
end
|
64
|
+
|
65
|
+
def write_node path, attributes
|
66
|
+
raise "not yet implemented"
|
67
|
+
end
|
68
|
+
|
69
|
+
def read path
|
70
|
+
url = "#{@target_url}#{path}"
|
71
|
+
open(url).read
|
72
|
+
rescue Timeout::Error => e
|
73
|
+
raise "connection timeout: #{url}"
|
74
|
+
rescue OpenURI::HTTPError => e
|
75
|
+
case code = e.to_s.to_i rescue 0
|
76
|
+
when 404
|
77
|
+
raise NameError, "undefined: #{path}"
|
78
|
+
else
|
79
|
+
puts " ## gom connection error: #{url} -- #{e}"
|
80
|
+
throw e
|
81
|
+
end
|
82
|
+
rescue => e
|
83
|
+
puts " ## read error: #{url} -- #{e}"
|
84
|
+
throw e
|
85
|
+
end
|
86
|
+
|
87
|
+
# update subscription observers. GNP callbacks will look like:
|
88
|
+
#
|
89
|
+
# http://<callback server>:<callback port>/gnp;<subscription name>;<subscription path>
|
90
|
+
#
|
91
|
+
def refresh
|
92
|
+
puts " -- refresh subscriptions(#{@subscriptions.size}):"
|
93
|
+
|
94
|
+
run_callback_server # call it once to make sure it runs
|
95
|
+
|
96
|
+
@subscriptions.each do |sub|
|
97
|
+
puts " - #{sub.name}"
|
98
|
+
params = { "attributes[accept]" => 'application/json' }
|
99
|
+
|
100
|
+
query = "/gnp;#{sub.name};#{sub.entry_uri}"
|
101
|
+
params["attributes[callback_url]"] = "#{callback_server.base_url}#{query}"
|
102
|
+
|
103
|
+
[:operations, :uri_regexp, :condition_script].each do |key|
|
104
|
+
(v = sub.send key) and params["attributes[#{key}]"] = v
|
105
|
+
end
|
106
|
+
|
107
|
+
url = "#{@target_url}#{sub.uri}"
|
108
|
+
http_put(url, params) # {|req| req.content_type = 'application/json'}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def subscribe sub
|
113
|
+
@subscriptions.delete sub # every sub only once!
|
114
|
+
@subscriptions.push sub
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def init_callback_server port
|
120
|
+
txt = (read "/gom/config/connection.txt")
|
121
|
+
unless m = (txt.match /^client_ip:\s*(\d+\.\d+\.\d+\.\d+)/)
|
122
|
+
raise "/gom/config/connection: No Client IP? '#{txt}'"
|
123
|
+
end
|
124
|
+
# this is the IP by which i am seen from the GOM
|
125
|
+
ip = m[1]
|
126
|
+
|
127
|
+
http = (HttpServer.new :host => ip, :port => port)
|
128
|
+
http.mount "^/gnp;", lambda {|*args| gnp_handler *args}
|
129
|
+
|
130
|
+
http
|
131
|
+
end
|
132
|
+
|
133
|
+
def run_callback_server
|
134
|
+
callback_server.start unless callback_server.running?
|
135
|
+
end
|
136
|
+
|
137
|
+
#def gnp_callback name, entry_uri, req
|
138
|
+
def gnp_handler request_uri, env
|
139
|
+
op, name, entry_uri = (request_uri.to_s.split /;/)
|
140
|
+
unless sub = @subscriptions.find { |s| s.name == name }
|
141
|
+
raise "no such subscription: #{name} :: #{entry_uri}"#\n#{@subscriptions.inspect}"
|
142
|
+
end
|
143
|
+
|
144
|
+
begin
|
145
|
+
req = Rack::Request.new(env)
|
146
|
+
op, payload = (decode_gnp_body req.body.read)
|
147
|
+
(sub.callback.call op, payload)
|
148
|
+
rescue Exception => e
|
149
|
+
callstack = "#{e.backtrace.join "\n "}"
|
150
|
+
puts " ## Subscription::callback - #{e}\n -> #{callstack}"
|
151
|
+
end
|
152
|
+
|
153
|
+
# HTTP OK keeps the subscription alive, even in case of handler errors
|
154
|
+
[200, {"Content-Type"=>"text/plain"}, ["keep going dude!"]]
|
155
|
+
end
|
156
|
+
|
157
|
+
def decode_gnp_body txt
|
158
|
+
debugger if (defined? debugger)
|
159
|
+
json = (JSON.parse txt)
|
160
|
+
puts " -- json GNP: #{json.inspect}"
|
161
|
+
|
162
|
+
payload = nil
|
163
|
+
op = %w{update delete create}.find { |op| json[op] }
|
164
|
+
%w{attribute node}.find { |t| payload = json[op][t] }
|
165
|
+
#puts "payload: #{payload.inspect}"
|
166
|
+
[op, payload]
|
167
|
+
|
168
|
+
#op = (json.include? 'update') ? :udpate : nil
|
169
|
+
#op ||= (json.include? 'delete') ? :delete : nil
|
170
|
+
#op ||= (json.include? 'create') ? :create : nil
|
171
|
+
#op or (raise "unknown GNP op: #{txt}")
|
172
|
+
|
173
|
+
#payload = json[op.to_s]
|
174
|
+
#[op, (payload['attribute'] || payload['node'])]
|
175
|
+
end
|
176
|
+
|
177
|
+
# incapsulates the underlying net access
|
178
|
+
def http_put(url, params, &request_modifier)
|
179
|
+
uri = URI.parse url
|
180
|
+
req = Net::HTTP::Put.new uri.path
|
181
|
+
req.set_form_data(params)
|
182
|
+
request_modifier && (request_modifier.call req)
|
183
|
+
|
184
|
+
session = (Net::HTTP.new uri.host, uri.port)
|
185
|
+
case res = session.start { |http| http.request req }
|
186
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
187
|
+
# OK
|
188
|
+
else
|
189
|
+
res.error!
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
module Gom
|
3
|
+
module Remote
|
4
|
+
class Daemon
|
5
|
+
|
6
|
+
include ::Timeout
|
7
|
+
|
8
|
+
Defaults = {
|
9
|
+
:actor_dt => 60,
|
10
|
+
:sensor_dt => 1,
|
11
|
+
:callback_port => 8815,
|
12
|
+
:stealth => false,
|
13
|
+
}
|
14
|
+
|
15
|
+
include OAttr
|
16
|
+
oattr :actor_dt, :sensor_dt, :stealth
|
17
|
+
|
18
|
+
# service_path is derived from the service_url (server part stripped)
|
19
|
+
attr :service_path
|
20
|
+
|
21
|
+
def initialize service_url, options = {}, &blk
|
22
|
+
@options = (Defaults.merge options)
|
23
|
+
callback_port = @options[:callback_port]
|
24
|
+
@gom, @service_path = (Connection.init service_url, callback_port)
|
25
|
+
(blk.call self, @service_path) unless blk.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
=begin
|
29
|
+
def open_nagios_port jjjjj
|
30
|
+
@gom.callback_server.mount(%r{^/nagios}, lambda do |*args|
|
31
|
+
uri, env = *args
|
32
|
+
req = Rack::Request.new(env)
|
33
|
+
envmap = (env.map { |k,v| "#{k}: #{v}" }.join "\n")
|
34
|
+
body = ["OK -- #{env['REQUEST_URI']}\n---\n#{envmap}"]
|
35
|
+
[200, {"Content-Type"=>"text/plain"}, body]
|
36
|
+
end)
|
37
|
+
end
|
38
|
+
hs = HttpServer.new o
|
39
|
+
hs.mount "^/gnp;", lambda {|*args| gnp_handler *args}
|
40
|
+
=end
|
41
|
+
def sensor_loop interval = sensor_dt, &tic
|
42
|
+
puts " -- running gom-script sensor loop.."
|
43
|
+
forever(interval) { tic.call self }
|
44
|
+
end
|
45
|
+
|
46
|
+
def actor_loop interval = actor_dt, &tic
|
47
|
+
puts " -- running gom-script actor loop.."
|
48
|
+
forever(interval) do
|
49
|
+
@gom.refresh
|
50
|
+
tic && (tic.call self) || :continue
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def forever interval = 0, &callback
|
55
|
+
loop do
|
56
|
+
begin
|
57
|
+
rc = callback.call
|
58
|
+
rescue Exception => e
|
59
|
+
puts " ## <#{e.class}> #{self} - #{e}\n -> #{e.backtrace.join "\n "}"
|
60
|
+
ensure
|
61
|
+
break if rc == :stop
|
62
|
+
sleep interval
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# push own ip and monitoring port back to GOM node
|
68
|
+
def check_in
|
69
|
+
if stealth
|
70
|
+
puts " -- no GOM check-in in stealth mode"
|
71
|
+
else
|
72
|
+
@gom.write "#{service_path}:daemon_ip", @gom.callback_server.host
|
73
|
+
#@gom.write "#{service_path}:nagios_port", nagios_port
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Gom
|
2
|
+
module Remote
|
3
|
+
class Entry
|
4
|
+
include Gom::Remote
|
5
|
+
def gom
|
6
|
+
Gom::Remote.connection
|
7
|
+
end
|
8
|
+
# @deprecated?
|
9
|
+
def connection
|
10
|
+
Gom::Remote.connection
|
11
|
+
end
|
12
|
+
|
13
|
+
def gnode path
|
14
|
+
json = (connection.read "#{path}.json")
|
15
|
+
(JSON.parse json)["node"]["entries"].select do |entry|
|
16
|
+
# 1. select attribute entries
|
17
|
+
entry.has_key? "attribute"
|
18
|
+
end.inject({}) do |h, a|
|
19
|
+
# 2. make it a key, value list
|
20
|
+
h[a["attribute"]["name"].to_sym] = a["attribute"]["value"]
|
21
|
+
h
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rack/handler/mongrel'
|
3
|
+
require 'thread'
|
4
|
+
require 'applix/oattr'
|
5
|
+
|
6
|
+
module Gom
|
7
|
+
module Remote
|
8
|
+
class HttpServer
|
9
|
+
|
10
|
+
Defaults = {
|
11
|
+
:host => "0.0.0.0", :port => 25191
|
12
|
+
}
|
13
|
+
|
14
|
+
include OAttr
|
15
|
+
oattr :host, :port
|
16
|
+
|
17
|
+
def initialize options = {}
|
18
|
+
@options = (Defaults.merge options)
|
19
|
+
@mounts = {}
|
20
|
+
@mounts_access = Mutex.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def base_url
|
24
|
+
p = (port == 80 ? '' : ":#{port}")
|
25
|
+
"http://#{host}#{p}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def running?
|
29
|
+
!@server.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
def mount pattern, handler
|
33
|
+
@mounts_access.synchronize { @mounts.update pattern => handler }
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def unmount pattern
|
38
|
+
@mounts_access.synchronize { @mounts.delete pattern }
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def start
|
43
|
+
@server.nil? or (raise "already running!")
|
44
|
+
@thread = Thread.new { start_mongrel_server }
|
45
|
+
sleep 0.1 # give thread time for start-up
|
46
|
+
@thread
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop
|
50
|
+
@server.nil? and (raise "not running!")
|
51
|
+
puts ' -- stopping callback server..'
|
52
|
+
@server.stop
|
53
|
+
@server = nil
|
54
|
+
puts ' down.'
|
55
|
+
sleep 2 # sleep added as a precaution
|
56
|
+
puts ' -- killing server thread now...'
|
57
|
+
@thread.kill
|
58
|
+
@thread = nil
|
59
|
+
puts ' and gone.'
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
# take the URI on walk it through the list of mounts and return the one
|
64
|
+
# with the longest match or nil. In case of a match the corresponding
|
65
|
+
# handler is returned, nil otherwise.
|
66
|
+
def match uri
|
67
|
+
targets = []
|
68
|
+
@mounts_access.synchronize do
|
69
|
+
targets = @mounts.map do |re, app|
|
70
|
+
[app, (uri.path.match re).to_s]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# sort for longest match. And target might be nil already for an empty
|
75
|
+
# targets list, which is ok as we return nil in that case.
|
76
|
+
target = targets.sort!{|a,b| a[1].length <=> b[1].length}.last
|
77
|
+
func, pattern = target
|
78
|
+
(pattern.nil? || pattern.empty?) ? nil : func
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# dispatching a request URI from env['REQUEST_URI'] which look
|
84
|
+
# somethings like:
|
85
|
+
#
|
86
|
+
# http://172.20.2.9:2719/gnp;enttec-dmx;/services/enttec-dmx-usb-pro/values
|
87
|
+
#
|
88
|
+
def dispatch env
|
89
|
+
#req = Rack::Request.new(env)
|
90
|
+
uri = (URI.parse env['REQUEST_URI'])
|
91
|
+
if func = (match uri)
|
92
|
+
func.call uri, env
|
93
|
+
else
|
94
|
+
puts " !! no handler for: #{req.fullpath} -- #{@mounts.keys.inspect}"
|
95
|
+
[404, {"Content-Type"=>"text/plain"}, ["Not Found"]]
|
96
|
+
end
|
97
|
+
rescue => e
|
98
|
+
puts " ## #{e}\n -> #{e.backtrace.join "\n "}"
|
99
|
+
[500, {"Content-Type"=>"text/plain"}, [e]]
|
100
|
+
end
|
101
|
+
|
102
|
+
# as i absolutly dislike capitalized options i use lowercase options
|
103
|
+
# throughout and only convert them just before i pass them the the
|
104
|
+
# mongrel server. Nothing to be proud of, but i definitly don't want to
|
105
|
+
# write --Port on the command line...
|
106
|
+
def mongrel_opts
|
107
|
+
@options.merge :Host => @options[:host], :Port => @options[:port]
|
108
|
+
end
|
109
|
+
|
110
|
+
def start_mongrel_server
|
111
|
+
puts " -- starting http server: #{@options.inspect}"
|
112
|
+
@server.nil? or (raise "already running!")
|
113
|
+
f = Proc.new {|env| dispatch env}
|
114
|
+
Rack::Handler::Mongrel.run(f, mongrel_opts) do |server|
|
115
|
+
@server = server
|
116
|
+
puts " mongrel up: #{@options.inspect}"
|
117
|
+
end
|
118
|
+
rescue Exception => e
|
119
|
+
puts " ## oops: #{e}"
|
120
|
+
puts @options.inspect
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Gom
|
2
|
+
module Remote
|
3
|
+
class Subscription
|
4
|
+
|
5
|
+
Defaults = {
|
6
|
+
:name => nil,
|
7
|
+
:operations => [:update],
|
8
|
+
:condition_script => nil,
|
9
|
+
:uri_regexp => nil,
|
10
|
+
:callback => nil,
|
11
|
+
}
|
12
|
+
|
13
|
+
attr_reader :entry_uri, :uri, :callback
|
14
|
+
attr_accessor :callback
|
15
|
+
attr_reader :name, :operations, :condition_script, :uri_regexp
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"#{self.class}: #{@options.inject}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# hint: supplying a recognizable name helps with distributed gom
|
22
|
+
# operations
|
23
|
+
#
|
24
|
+
def initialize entry_uri, options = {}, &blk
|
25
|
+
@name = options[:name] || "0x#{object_id}"
|
26
|
+
# URI for the observer node
|
27
|
+
@uri = "/gom/observer#{entry_uri.sub ':', '/'}/.#{@name}"
|
28
|
+
|
29
|
+
@options = Defaults.merge options
|
30
|
+
@entry_uri = entry_uri
|
31
|
+
@callback = options[:callback] || blk;
|
32
|
+
@operations = (@options[:operations] || []).join ', '
|
33
|
+
@uri_regexp = (re = @options[:uri_regexp]) && (Regexp.new re) || nil
|
34
|
+
@condition_script = @options[:condition_script]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../../spec_helper'
|
2
|
+
|
3
|
+
describe Gom::Remote::Connection do
|
4
|
+
|
5
|
+
describe "initialization" do
|
6
|
+
it "should split a GOM node url" do
|
7
|
+
gom, path = (Gom::Remote::Connection.split_url 'http://gom:345/dmx/node')
|
8
|
+
gom.should == 'http://gom:345'
|
9
|
+
path.should == '/dmx/node'
|
10
|
+
end
|
11
|
+
it "should strip last slash from the node" do
|
12
|
+
gom, path = (Gom::Remote::Connection.split_url 'http://xxx:4321/a/b/c/')
|
13
|
+
gom.should == 'http://xxx:4321'
|
14
|
+
path.should == '/a/b/c'
|
15
|
+
gom, path = (Gom::Remote::Connection.split_url 'http://xxx:4321/a/b:c/')
|
16
|
+
gom.should == 'http://xxx:4321'
|
17
|
+
path.should == '/a/b:c'
|
18
|
+
end
|
19
|
+
it "should split an attribute URL" do
|
20
|
+
gom, path = (Gom::Remote::Connection.split_url 'http://xxx/a:x')
|
21
|
+
gom.should == 'http://xxx'
|
22
|
+
path.should == '/a:x'
|
23
|
+
end
|
24
|
+
it "should split a GOM node url on init" do
|
25
|
+
gom, path = (Gom::Remote::Connection.init 'http://gom:345/dmx/node')
|
26
|
+
gom.target_url.should == 'http://gom:345'
|
27
|
+
path.should == '/dmx/node'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "with a connection it" do
|
32
|
+
before :each do
|
33
|
+
@gom, path = (Gom::Remote::Connection.init 'http://gom:345/dmx/node')
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should fetch the callback_ip from remote" do
|
37
|
+
@gom.callback_server.host.should == "10.0.0.23"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should put attribute values to remote" do
|
41
|
+
@gom.should_receive(:http_put).
|
42
|
+
with("http://gom:345/some/node:attr", { "attribute" => "abc", "type" => :string })
|
43
|
+
@gom.write '/some/node:attr', "abc"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "with subscriptions" do
|
48
|
+
before :each do
|
49
|
+
@gom, path = (Gom::Remote::Connection.init 'http://localhost:3000')
|
50
|
+
@gom.stub!(:run_callback_server).and_return(true)
|
51
|
+
end
|
52
|
+
|
53
|
+
#it "should have no subscriptions on init" do
|
54
|
+
# @gom.subscriptions.should == []
|
55
|
+
#end
|
56
|
+
|
57
|
+
it "should subscribe operations whitelist" do
|
58
|
+
s = (Gom::Remote::Subscription.new '/node', :operations => [:delete, :create])
|
59
|
+
@gom.should_receive(:http_put).with(
|
60
|
+
"http://localhost:3000/gom/observer/node/.#{s.name}",
|
61
|
+
hash_including("attributes[operations]" => 'delete, create')
|
62
|
+
)
|
63
|
+
@gom.subscribe s
|
64
|
+
@gom.refresh
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should have an uri regexp" do
|
68
|
+
s = (Gom::Remote::Subscription.new '/node', :uri_regexp => /foo/)
|
69
|
+
@gom.should_receive(:http_put).with(
|
70
|
+
"http://localhost:3000/gom/observer/node/.#{s.name}",
|
71
|
+
hash_including("attributes[uri_regexp]" => /foo/)
|
72
|
+
)
|
73
|
+
@gom.subscribe s
|
74
|
+
@gom.refresh
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should have accept=application/json param" do
|
78
|
+
s = (Gom::Remote::Subscription.new '/node')
|
79
|
+
@gom.should_receive(:http_put).with(
|
80
|
+
"http://localhost:3000/gom/observer/node/.#{s.name}",
|
81
|
+
hash_including("attributes[accept]" => 'application/json')
|
82
|
+
)
|
83
|
+
@gom.subscribe s
|
84
|
+
@gom.refresh
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should put observer to gom on refresh" do
|
88
|
+
s = (Gom::Remote::Subscription.new '/node/values')
|
89
|
+
@gom.should_receive(:http_put).with(
|
90
|
+
"http://localhost:3000/gom/observer/node/values/.#{s.name}",
|
91
|
+
#hash_including("attributes[callback_url]" => "http://1.2.3.4:2179/gnp;#{s.name};/node/values")
|
92
|
+
hash_including("attributes[callback_url]" => anything)
|
93
|
+
)
|
94
|
+
@gom.subscribe s
|
95
|
+
@gom.refresh
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should observe an attribute entry" do
|
99
|
+
s = (Gom::Remote::Subscription.new '/node:attribute')
|
100
|
+
@gom.should_receive(:http_put).with(
|
101
|
+
"http://localhost:3000/gom/observer/node/attribute/.#{s.name}",
|
102
|
+
#hash_including("attributes[callback_url]" => "http://1.2.3.4:2179/gnp;#{s.name};/node:attribute")
|
103
|
+
hash_including("attributes[callback_url]" => anything)
|
104
|
+
)
|
105
|
+
@gom.subscribe s
|
106
|
+
@gom.refresh
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../../spec_helper'
|
2
|
+
|
3
|
+
include Gom::Remote
|
4
|
+
|
5
|
+
describe Gom::Remote::Daemon do
|
6
|
+
|
7
|
+
describe "with a plain vanilla daemon" do
|
8
|
+
before :each do
|
9
|
+
@daemon = Daemon.new 'http://gom:345/gom-script/test'
|
10
|
+
end
|
11
|
+
it "should have a default actor_dt" do
|
12
|
+
@daemon.actor_dt.should == Daemon::Defaults[:actor_dt]
|
13
|
+
end
|
14
|
+
it "should have stealth mode off by default" do
|
15
|
+
@daemon.stealth.should == false
|
16
|
+
end
|
17
|
+
it "should have a default sensor_dt" do
|
18
|
+
@daemon.sensor_dt.should == Daemon::Defaults[:sensor_dt]
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should parse the service_path from the service_url" do
|
22
|
+
@daemon.service_path.should == '/gom-script/test'
|
23
|
+
end
|
24
|
+
it "should terminate sensor loop on :stop" do
|
25
|
+
count = 0
|
26
|
+
timeout(1) do
|
27
|
+
@daemon.sensor_loop(0.1) { count += 1; :stop if count == 3 }
|
28
|
+
end
|
29
|
+
count.should == 3
|
30
|
+
end
|
31
|
+
it "should terminate actor loop on :stop" do
|
32
|
+
Gom::Remote.connection.should_receive(:refresh)
|
33
|
+
timeout(1) { @daemon.actor_loop { :stop } }
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should check in with its client ip" do
|
37
|
+
Gom::Remote.connection.should_receive(:write).
|
38
|
+
with("/gom-script/test:daemon_ip", "10.0.0.23")
|
39
|
+
@daemon.check_in
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "initialization" do
|
44
|
+
it "should find the class" do
|
45
|
+
Daemon.should_not == nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should init the connection" do
|
49
|
+
Gom::Remote.connection.should == nil
|
50
|
+
Daemon.new 'http://gom:345/gom-script/test'
|
51
|
+
(c = Gom::Remote.connection).should_not == nil
|
52
|
+
c.target_url.should == 'http://gom:345'
|
53
|
+
c.initial_path.should == '/gom-script/test'
|
54
|
+
c.callback_server.port.should == Daemon::Defaults[:callback_port]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../../spec_helper'
|
2
|
+
|
3
|
+
describe Gom::Remote::HttpServer do
|
4
|
+
|
5
|
+
describe "initialization" do
|
6
|
+
it "should not be running on creation" do
|
7
|
+
server = Gom::Remote::HttpServer.new
|
8
|
+
server.running?.should == false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should overwrite host option" do
|
13
|
+
server = Gom::Remote::HttpServer.new :host => "1.2.3.4"
|
14
|
+
server.host.should == "1.2.3.4"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should overwrite port option" do
|
18
|
+
server = Gom::Remote::HttpServer.new :port => 9151
|
19
|
+
server.port.should == 9151
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have a base url" do
|
23
|
+
h, p = (Gom::Remote::HttpServer::Defaults.values_at :host, :port)
|
24
|
+
|
25
|
+
server = Gom::Remote::HttpServer.new
|
26
|
+
server.base_url.should == "http://#{h}:#{p}"
|
27
|
+
server = Gom::Remote::HttpServer.new :port => 9151
|
28
|
+
server.base_url.should == "http://#{h}:9151"
|
29
|
+
server = Gom::Remote::HttpServer.new :host => '127.0.0.1'
|
30
|
+
server.base_url.should == "http://127.0.0.1:#{p}"
|
31
|
+
server = Gom::Remote::HttpServer.new :port => 1234, :host => '10.1.1.10'
|
32
|
+
server.base_url.should == "http://10.1.1.10:1234"
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "with a server" do
|
36
|
+
before :each do
|
37
|
+
@server = Gom::Remote::HttpServer.new
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should match empty mounts to nil" do
|
41
|
+
@server.send(:match, (URI.parse '/foo/aa;bb;cc?p1=12&p2=oo')).should == nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should not missmatch" do
|
45
|
+
@server.mount '^/a', lambda { }
|
46
|
+
@server.mount '^/a/b', lambda { }
|
47
|
+
@server.mount '^/a/b/c', lambda { }
|
48
|
+
@server.match(URI.parse '/x/a/b/c').should == nil
|
49
|
+
@server.match(URI.parse '/x/a/b').should == nil
|
50
|
+
@server.match(URI.parse '/x/a').should == nil
|
51
|
+
@server.match(URI.parse 'a').should == nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should unmount" do
|
55
|
+
@server.mount '/a/b/c', (l = lambda { "block 6" })
|
56
|
+
@server.match(URI.parse '/a/b/c/d').should == l
|
57
|
+
@server.unmount '/a/b/c'
|
58
|
+
@server.match(URI.parse '/a/b/c/d').should == nil
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should match with regexp as well" do
|
62
|
+
@server.mount %r{/a}, (l1 = lambda { "block 1" })
|
63
|
+
@server.mount %r{/a/b}, (l2 = lambda { "block 2" })
|
64
|
+
@server.mount %r{/a/b/c}, (l3 = lambda { "block 3" })
|
65
|
+
@server.match(URI.parse '/a/b').should == l2
|
66
|
+
@server.match(URI.parse '/a/b/c').should == l3
|
67
|
+
@server.match(URI.parse '/a/b/x').should == l2
|
68
|
+
@server.match(URI.parse '/a/b/c/d').should == l3
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should prefer longer matches" do
|
72
|
+
@server.mount '/a', (l1 = lambda { "block 1" })
|
73
|
+
@server.mount '/a/b', (l2 = lambda { "block 2" })
|
74
|
+
@server.mount '/a/b/c', (l3 = lambda { "block 3" })
|
75
|
+
@server.match(URI.parse '/a/b').should == l2
|
76
|
+
@server.match(URI.parse '/a/b/c').should == l3
|
77
|
+
@server.match(URI.parse '/a/b/x').should == l2
|
78
|
+
@server.match(URI.parse '/a/b/c/d').should == l3
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should match simple strings" do
|
82
|
+
@server.mount "/foo", (l = lambda { puts "needs some code here" })
|
83
|
+
@server.match(URI.parse '/foo/aa;bb;cc?p1=12&p2=oo').should == l
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should have default mongrel options" do
|
87
|
+
@server.port.should == Gom::Remote::HttpServer::Defaults[:port]
|
88
|
+
@server.host.should == Gom::Remote::HttpServer::Defaults[:host]
|
89
|
+
end
|
90
|
+
|
91
|
+
#it "should dispatch a nagios callback" do
|
92
|
+
# @cs.should_not_receive(:gnp_dispatcher)
|
93
|
+
# response = @cs.send(:dispatch_request_uri, "/nagios;foo;bar", {})
|
94
|
+
# response.should == [200, {"Content-Type"=>"text/plain"}, ["OK"]]
|
95
|
+
#end
|
96
|
+
|
97
|
+
it "should start and stop" do
|
98
|
+
@server.start.class.should == Thread
|
99
|
+
#sleep 1
|
100
|
+
@server.running?.should == true
|
101
|
+
@server.stop.should == @server
|
102
|
+
sleep 1
|
103
|
+
@server.running?.should == false
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../../spec_helper'
|
2
|
+
|
3
|
+
describe Gom::Remote::Subscription do
|
4
|
+
|
5
|
+
describe "when created with default options" do
|
6
|
+
before :each do
|
7
|
+
@sub = (Gom::Remote::Subscription.new '/dmx/node/values')
|
8
|
+
end
|
9
|
+
it "should have a object id as name" do
|
10
|
+
@sub.name.should == "0x#{@sub.object_id}"
|
11
|
+
end
|
12
|
+
it "should have operations whitelist" do
|
13
|
+
@sub.operations.should == "update"
|
14
|
+
end
|
15
|
+
it "should have nil condition_script" do
|
16
|
+
@sub.condition_script.should == nil
|
17
|
+
end
|
18
|
+
it "should have a nil uri_regexp" do
|
19
|
+
@sub.uri_regexp.should == nil
|
20
|
+
end
|
21
|
+
it "should have a nil callback" do
|
22
|
+
@sub.callback.should == nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should overwrite callback from options" do
|
27
|
+
callback = lambda {}
|
28
|
+
s = (Gom::Remote::Subscription.new '/node/values', :callback => callback )
|
29
|
+
s.callback.should == callback
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should overwrite name from options value" do
|
33
|
+
name = "test-#{Time.now.to_i}"
|
34
|
+
s = (Gom::Remote::Subscription.new '/node/values', :name => name)
|
35
|
+
s.name.should == name
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "observer uri" do
|
39
|
+
it "should construct a proper gom observer uri" do
|
40
|
+
s = (Gom::Remote::Subscription.new '/dmx/node/values')
|
41
|
+
s.uri.should == "/gom/observer/dmx/node/values/.#{s.name}"
|
42
|
+
end
|
43
|
+
it "should interpret attribute paths" do
|
44
|
+
s = (Gom::Remote::Subscription.new '/dmx/node:attribute')
|
45
|
+
s.uri.should == "/gom/observer/dmx/node/attribute/.#{s.name}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--backtrace --debugger
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# figure out where we are being loaded from
|
2
|
+
if $LOADED_FEATURES.grep(/spec\/spec_helper\.rb/).any?
|
3
|
+
begin
|
4
|
+
raise "foo"
|
5
|
+
rescue => e
|
6
|
+
puts <<-MSG
|
7
|
+
===================================================
|
8
|
+
It looks like spec_helper.rb has been loaded
|
9
|
+
multiple times. Normalize the require to:
|
10
|
+
|
11
|
+
require "spec/spec_helper"
|
12
|
+
|
13
|
+
Things like File.join and File.expand_path will
|
14
|
+
cause it to be loaded multiple times.
|
15
|
+
|
16
|
+
Loaded this time from:
|
17
|
+
|
18
|
+
#{e.backtrace.join("\n ")}
|
19
|
+
===================================================
|
20
|
+
MSG
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
25
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
26
|
+
require 'spec'
|
27
|
+
require 'spec/autorun'
|
28
|
+
require 'fakeweb'
|
29
|
+
|
30
|
+
require 'gom-script'
|
31
|
+
|
32
|
+
Spec::Runner.configure do |config|
|
33
|
+
config.before :each do
|
34
|
+
FakeWeb.register_uri(
|
35
|
+
:get, "http://gom:345/gom/config/connection.txt",
|
36
|
+
:body => "client_ip: 10.0.0.23"
|
37
|
+
)
|
38
|
+
FakeWeb.register_uri(
|
39
|
+
:get, "http://localhost:3000/gom/config/connection.txt",
|
40
|
+
:body => "client_ip: 10.0.0.23"
|
41
|
+
)
|
42
|
+
|
43
|
+
Gom::Remote.connection = nil # reset for every test
|
44
|
+
end
|
45
|
+
|
46
|
+
config.after :each do
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gom-script
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- art+com/dirk luesebrink
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-30 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: applix
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.2.1
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rack
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: gom-core
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: rspec
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: fakeweb
|
57
|
+
type: :development
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 1.2.7
|
64
|
+
version:
|
65
|
+
description: " \n GOM is a schema-less object database in ruby with Resource Oriented API,\n server-side javascript, distributed HTTP notifications and some more.\n This gom-script script simplifies coding of clients and daemon which like\n to listen on state change event in the GOM.\n "
|
66
|
+
email: dirk.luesebrink@gmail.com
|
67
|
+
executables: []
|
68
|
+
|
69
|
+
extensions: []
|
70
|
+
|
71
|
+
extra_rdoc_files:
|
72
|
+
- LICENSE
|
73
|
+
- README.markdown
|
74
|
+
files:
|
75
|
+
- .document
|
76
|
+
- .gitignore
|
77
|
+
- LICENSE
|
78
|
+
- README.markdown
|
79
|
+
- Rakefile
|
80
|
+
- VERSION
|
81
|
+
- lib/gom-script.rb
|
82
|
+
- lib/gom/remote.rb
|
83
|
+
- lib/gom/remote/connection.rb
|
84
|
+
- lib/gom/remote/daemon.rb
|
85
|
+
- lib/gom/remote/entry.rb
|
86
|
+
- lib/gom/remote/http_server.rb
|
87
|
+
- lib/gom/remote/subscription.rb
|
88
|
+
- spec/gom/remote/connection_spec.rb
|
89
|
+
- spec/gom/remote/daemon_spec.rb
|
90
|
+
- spec/gom/remote/http_server_spec.rb
|
91
|
+
- spec/gom/remote/subscription_spec.rb
|
92
|
+
- spec/spec.opts
|
93
|
+
- spec/spec_helper.rb
|
94
|
+
has_rdoc: true
|
95
|
+
homepage: http://github.com/crux/gom-script
|
96
|
+
licenses: []
|
97
|
+
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options:
|
100
|
+
- --charset=UTF-8
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: "0"
|
108
|
+
version:
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: "0"
|
114
|
+
version:
|
115
|
+
requirements: []
|
116
|
+
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 1.3.5
|
119
|
+
signing_key:
|
120
|
+
specification_version: 3
|
121
|
+
summary: connecting scripts and daemons with a remote GOM instance
|
122
|
+
test_files:
|
123
|
+
- spec/gom/remote/connection_spec.rb
|
124
|
+
- spec/gom/remote/daemon_spec.rb
|
125
|
+
- spec/gom/remote/http_server_spec.rb
|
126
|
+
- spec/gom/remote/subscription_spec.rb
|
127
|
+
- spec/spec_helper.rb
|