ffwd 0.1.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.
- checksums.yaml +7 -0
- data/bin/ffwd +9 -0
- data/bin/fwc +15 -0
- data/lib/em/all.rb +68 -0
- data/lib/ffwd.rb +250 -0
- data/lib/ffwd/channel.rb +62 -0
- data/lib/ffwd/circular_buffer.rb +78 -0
- data/lib/ffwd/connection.rb +40 -0
- data/lib/ffwd/core.rb +173 -0
- data/lib/ffwd/core/emitter.rb +38 -0
- data/lib/ffwd/core/interface.rb +47 -0
- data/lib/ffwd/core/processor.rb +92 -0
- data/lib/ffwd/core/reporter.rb +32 -0
- data/lib/ffwd/debug.rb +76 -0
- data/lib/ffwd/debug/connection.rb +48 -0
- data/lib/ffwd/debug/monitor_session.rb +71 -0
- data/lib/ffwd/debug/tcp.rb +82 -0
- data/lib/ffwd/event.rb +65 -0
- data/lib/ffwd/event_emitter.rb +57 -0
- data/lib/ffwd/handler.rb +43 -0
- data/lib/ffwd/lifecycle.rb +92 -0
- data/lib/ffwd/logging.rb +139 -0
- data/lib/ffwd/metric.rb +55 -0
- data/lib/ffwd/metric_emitter.rb +50 -0
- data/lib/ffwd/plugin.rb +149 -0
- data/lib/ffwd/plugin/json_line.rb +47 -0
- data/lib/ffwd/plugin/json_line/connection.rb +118 -0
- data/lib/ffwd/plugin/log.rb +35 -0
- data/lib/ffwd/plugin/log/writer.rb +42 -0
- data/lib/ffwd/plugin_channel.rb +64 -0
- data/lib/ffwd/plugin_loader.rb +121 -0
- data/lib/ffwd/processor.rb +96 -0
- data/lib/ffwd/processor/count.rb +109 -0
- data/lib/ffwd/processor/histogram.rb +200 -0
- data/lib/ffwd/processor/rate.rb +116 -0
- data/lib/ffwd/producing_client.rb +181 -0
- data/lib/ffwd/protocol.rb +28 -0
- data/lib/ffwd/protocol/tcp.rb +126 -0
- data/lib/ffwd/protocol/tcp/bind.rb +64 -0
- data/lib/ffwd/protocol/tcp/connection.rb +107 -0
- data/lib/ffwd/protocol/tcp/flushing_connect.rb +135 -0
- data/lib/ffwd/protocol/tcp/plain_connect.rb +74 -0
- data/lib/ffwd/protocol/udp.rb +48 -0
- data/lib/ffwd/protocol/udp/bind.rb +64 -0
- data/lib/ffwd/protocol/udp/connect.rb +110 -0
- data/lib/ffwd/reporter.rb +65 -0
- data/lib/ffwd/retrier.rb +72 -0
- data/lib/ffwd/schema.rb +92 -0
- data/lib/ffwd/schema/default.rb +36 -0
- data/lib/ffwd/schema/spotify100.rb +58 -0
- data/lib/ffwd/statistics.rb +29 -0
- data/lib/ffwd/statistics/collector.rb +99 -0
- data/lib/ffwd/statistics/system_statistics.rb +255 -0
- data/lib/ffwd/tunnel.rb +27 -0
- data/lib/ffwd/tunnel/plugin.rb +47 -0
- data/lib/ffwd/tunnel/tcp.rb +60 -0
- data/lib/ffwd/tunnel/udp.rb +61 -0
- data/lib/ffwd/utils.rb +46 -0
- data/lib/ffwd/version.rb +18 -0
- data/lib/fwc.rb +206 -0
- metadata +163 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require_relative '../../reporter'
|
17
|
+
|
18
|
+
module FFWD::TCP
|
19
|
+
class PlainConnect
|
20
|
+
include FFWD::Reporter
|
21
|
+
|
22
|
+
setup_reporter :keys => [
|
23
|
+
:dropped_events, :dropped_metrics,
|
24
|
+
:sent_events, :sent_metrics,
|
25
|
+
:failed_events, :failed_metrics
|
26
|
+
]
|
27
|
+
|
28
|
+
attr_reader :log
|
29
|
+
|
30
|
+
def reporter_meta
|
31
|
+
@c.reporter_meta
|
32
|
+
end
|
33
|
+
|
34
|
+
INITIAL_TIMEOUT = 2
|
35
|
+
|
36
|
+
def initialize core, log, connection
|
37
|
+
@log = log
|
38
|
+
@c = connection
|
39
|
+
|
40
|
+
subs = []
|
41
|
+
|
42
|
+
core.starting do
|
43
|
+
@c.connect
|
44
|
+
subs << core.output.event_subscribe{|e| handle_event e}
|
45
|
+
subs << core.output.metric_subscribe{|e| handle_metric e}
|
46
|
+
end
|
47
|
+
|
48
|
+
core.stopping do
|
49
|
+
@c.disconnect
|
50
|
+
subs.each(&:unsubscribe).clear
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def handle_event event
|
55
|
+
return increment :dropped_events, 1 unless @c.writable?
|
56
|
+
@c.send_event event
|
57
|
+
increment :sent_events, 1
|
58
|
+
rescue => e
|
59
|
+
log.error "Failed to handle event", e
|
60
|
+
log.error "The following event could not be flushed: #{event.to_h}"
|
61
|
+
increment :failed_events, 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def handle_metric metric
|
65
|
+
return increment :dropped_metrics, 1 unless @c.writable?
|
66
|
+
@c.send_metric metric
|
67
|
+
increment :sent_metrics, 1
|
68
|
+
rescue => e
|
69
|
+
log.error "Failed to handle metric", e
|
70
|
+
log.error "The following metric could not be flushed: #{metric.to_h}"
|
71
|
+
increment :failed_metrics, 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require 'eventmachine'
|
17
|
+
|
18
|
+
require_relative 'udp/connect'
|
19
|
+
require_relative 'udp/bind'
|
20
|
+
|
21
|
+
require_relative '../tunnel'
|
22
|
+
|
23
|
+
module FFWD::UDP
|
24
|
+
def self.family
|
25
|
+
:udp
|
26
|
+
end
|
27
|
+
|
28
|
+
DEFAULT_REBIND_TIMEOUT = 10
|
29
|
+
|
30
|
+
def self.connect opts, core, log, handler
|
31
|
+
raise "Missing required key :host" if (host = opts[:host]).nil?
|
32
|
+
raise "Missing required key :port" if (port = opts[:port]).nil?
|
33
|
+
Connect.new core, log, host, port, handler
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.bind opts, core, log, connection, *args
|
37
|
+
raise "Missing required key :host" if (host = opts[:host]).nil?
|
38
|
+
raise "Missing required key :port" if (port = opts[:port]).nil?
|
39
|
+
rebind_timeout = opts[:rebind_timeout] || DEFAULT_REBIND_TIMEOUT
|
40
|
+
Bind.new core, log, host, port, connection, args, rebind_timeout
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.tunnel opts, core, plugin, log, connection, *args
|
44
|
+
raise "Missing required key :port" if (port = opts[:port]).nil?
|
45
|
+
FFWD.tunnel self.family, port, core, plugin, log, connection, args
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require 'eventmachine'
|
17
|
+
|
18
|
+
require_relative '../../reporter'
|
19
|
+
require_relative '../../retrier'
|
20
|
+
|
21
|
+
module FFWD::UDP
|
22
|
+
class Bind
|
23
|
+
include FFWD::Reporter
|
24
|
+
|
25
|
+
setup_reporter :keys => [
|
26
|
+
:received_events, :received_metrics,
|
27
|
+
:failed_events, :failed_metrics
|
28
|
+
]
|
29
|
+
|
30
|
+
attr_reader :reporter_meta
|
31
|
+
|
32
|
+
def initialize core, log, host, port, connection, args, rebind_timeout
|
33
|
+
@peer = "#{host}:#{port}"
|
34
|
+
@reporter_meta = {
|
35
|
+
:type => connection.plugin_type,
|
36
|
+
:listen => @peer, :family => 'udp'
|
37
|
+
}
|
38
|
+
|
39
|
+
@sig = nil
|
40
|
+
|
41
|
+
info = "udp://#{@peer}"
|
42
|
+
|
43
|
+
r = FFWD.retry :timeout => rebind_timeout do |a|
|
44
|
+
@sig = EM.open_datagram_socket host, port, connection, self, core, *args
|
45
|
+
log.info "Bind on #{info} (attempt #{a})"
|
46
|
+
end
|
47
|
+
|
48
|
+
r.error do |a, t, e|
|
49
|
+
log.error "Failed to bind #{info} (attempt #{a}), retry in #{t}s", e
|
50
|
+
end
|
51
|
+
|
52
|
+
r.depend_on core
|
53
|
+
|
54
|
+
core.stopping do
|
55
|
+
log.info "Unbinding #{info}"
|
56
|
+
|
57
|
+
if @sig
|
58
|
+
@sig.unbind
|
59
|
+
@sig = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require_relative '../../reporter'
|
17
|
+
require_relative '../../retrier'
|
18
|
+
|
19
|
+
module FFWD::UDP
|
20
|
+
class Connect
|
21
|
+
include FFWD::Reporter
|
22
|
+
|
23
|
+
attr_reader :reporter_meta, :log
|
24
|
+
|
25
|
+
setup_reporter :keys => [
|
26
|
+
:dropped_events, :dropped_metrics,
|
27
|
+
:sent_events, :sent_metrics
|
28
|
+
]
|
29
|
+
|
30
|
+
def initialize core, log, host, port, handler
|
31
|
+
@log = log
|
32
|
+
@host = host
|
33
|
+
@port = port
|
34
|
+
@handler = handler
|
35
|
+
|
36
|
+
@bind_host = "0.0.0.0"
|
37
|
+
@host_ip = nil
|
38
|
+
@c = nil
|
39
|
+
@peer = "#{host}:#{port}"
|
40
|
+
@reporter_meta = {
|
41
|
+
:type => @handler.name, :peer => peer
|
42
|
+
}
|
43
|
+
|
44
|
+
info = "udp://#{@peer}"
|
45
|
+
|
46
|
+
subs = []
|
47
|
+
|
48
|
+
r = FFWD.retry :timeout => resolve_timeout do |a|
|
49
|
+
unless @host_ip
|
50
|
+
@host_ip = resolve_ip @host
|
51
|
+
raise "Could not resolve: #{@host}" if @host_ip.nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
@c = EM.open_datagram_socket(@bind_host, nil)
|
55
|
+
log.info "Setup of output to #{info} successful"
|
56
|
+
|
57
|
+
subs << core.output.event_subscribe{|e| handle_event e}
|
58
|
+
subs << core.output.metric_subscribe{|m| handle_metric m}
|
59
|
+
end
|
60
|
+
|
61
|
+
r.error do |a, t, e|
|
62
|
+
log.error "Setup of output to #{info} failed (attempt #{a}), retry in #{t}s", e
|
63
|
+
end
|
64
|
+
|
65
|
+
r.depend_on core
|
66
|
+
|
67
|
+
core.stopping do
|
68
|
+
if @c
|
69
|
+
@c.close
|
70
|
+
@c = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
subs.each(&:unsubscribe).clear
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def handle_event event
|
80
|
+
unless @c
|
81
|
+
increment :dropped_events, 1
|
82
|
+
return
|
83
|
+
end
|
84
|
+
|
85
|
+
data = @handler.serialize_event event
|
86
|
+
@c.send_datagram data, @host_ip, @port
|
87
|
+
increment :sent_events, 1
|
88
|
+
end
|
89
|
+
|
90
|
+
def handle_metric metric
|
91
|
+
unless @c
|
92
|
+
increment :dropped_metrics, 1
|
93
|
+
return
|
94
|
+
end
|
95
|
+
|
96
|
+
data = @handler.serialize_metric metric
|
97
|
+
@c.send_datagram data, @host_ip, @port
|
98
|
+
increment :sent_metrics, 1
|
99
|
+
end
|
100
|
+
|
101
|
+
def resolve_ip host
|
102
|
+
Socket.getaddrinfo(@host, nil, nil, :DGRAM).each do |item|
|
103
|
+
next if item[0] != "AF_INET"
|
104
|
+
return item[3]
|
105
|
+
end
|
106
|
+
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
module FFWD::Reporter
|
17
|
+
def self.map_meta meta
|
18
|
+
Hash[meta.map{|k, v| [k.to_s, v]}]
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def reporter_keys
|
23
|
+
@_reporter_keys ||= [:total]
|
24
|
+
end
|
25
|
+
|
26
|
+
def reporter_meta
|
27
|
+
@_reporter_meta ||= nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def reporter_meta_method
|
31
|
+
@_reporter_meta_method ||= :reporter_meta
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup_reporter opts={}
|
35
|
+
@_reporter_keys = [:total] + (opts[:keys] || [])
|
36
|
+
@_reporter_meta = opts[:reporter_meta]
|
37
|
+
@_reporter_meta_method = opts[:id_method] || :reporter_meta
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.included mod
|
42
|
+
mod.extend ClassMethods
|
43
|
+
end
|
44
|
+
|
45
|
+
def reporter_data
|
46
|
+
@_reporter_keys ||= self.class.reporter_keys
|
47
|
+
@_reporter_data ||= Hash[@_reporter_keys.map{|k| [k, 0]}]
|
48
|
+
end
|
49
|
+
|
50
|
+
def increment n, c=1
|
51
|
+
reporter_data[n] += c
|
52
|
+
reporter_data[:total] += c
|
53
|
+
end
|
54
|
+
|
55
|
+
def report!
|
56
|
+
@_reporter_meta ||= FFWD::Reporter.map_meta(
|
57
|
+
self.class.reporter_meta || send(self.class.reporter_meta_method))
|
58
|
+
|
59
|
+
reporter_data.each do |k, v|
|
60
|
+
yield(:key => k, :value => v,
|
61
|
+
:meta => @_reporter_meta)
|
62
|
+
reporter_data[k] = 0
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/ffwd/retrier.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require_relative 'logging'
|
17
|
+
require_relative 'lifecycle'
|
18
|
+
|
19
|
+
module FFWD
|
20
|
+
# Try to execute a block on an exponential timer until it no longer throws
|
21
|
+
# an exception.
|
22
|
+
class Retrier
|
23
|
+
include FFWD::Lifecycle
|
24
|
+
|
25
|
+
def initialize timeout, &block
|
26
|
+
@block = block
|
27
|
+
@timer = nil
|
28
|
+
@timeout = timeout
|
29
|
+
@current_timeout = @timeout
|
30
|
+
@attempt = 0
|
31
|
+
@error_callbacks = []
|
32
|
+
|
33
|
+
starting do
|
34
|
+
try_block
|
35
|
+
end
|
36
|
+
|
37
|
+
stopping do
|
38
|
+
if @timer
|
39
|
+
@timer.cancel
|
40
|
+
@timer = nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def error &block
|
46
|
+
@error_callbacks << block
|
47
|
+
end
|
48
|
+
|
49
|
+
def try_block
|
50
|
+
@attempt += 1
|
51
|
+
@block.call @attempt
|
52
|
+
@current_timeout = @timeout
|
53
|
+
rescue => e
|
54
|
+
@error_callbacks.each do |block|
|
55
|
+
block.call @attempt, @current_timeout, e
|
56
|
+
end
|
57
|
+
|
58
|
+
@timer = EM::Timer.new(@current_timeout) do
|
59
|
+
@current_timeout *= 2
|
60
|
+
@timer = nil
|
61
|
+
try_block
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
DEFAULT_TIMEOUT = 10
|
67
|
+
|
68
|
+
def self.retry opts={}, &block
|
69
|
+
timeout = opts[:timeout] || DEFAULT_TIMEOUT
|
70
|
+
Retrier.new(timeout, &block)
|
71
|
+
end
|
72
|
+
end
|
data/lib/ffwd/schema.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
module FFWD
|
17
|
+
SCHEMA_DEFAULT_SUPPORT = [
|
18
|
+
:dump_metric,
|
19
|
+
:dump_event
|
20
|
+
]
|
21
|
+
|
22
|
+
DEFUALT_SCHEMA = 'default'
|
23
|
+
DEFAULT_CONTENT_TYPE = 'application/json'
|
24
|
+
|
25
|
+
def self.parse_schema opts, support=SCHEMA_DEFAULT_SUPPORT
|
26
|
+
name = opts[:schema] || DEFUALT_SCHEMA
|
27
|
+
content_type = opts[:content_type] || DEFAULT_CONTENT_TYPE
|
28
|
+
key = [name, content_type]
|
29
|
+
|
30
|
+
schema = FFWD::Schema.loaded[key]
|
31
|
+
|
32
|
+
if schema.nil?
|
33
|
+
raise "No schema '#{name}' for content type '#{content_type}'"
|
34
|
+
end
|
35
|
+
|
36
|
+
unless schema.support? support
|
37
|
+
raise "Schema #{schema} does not support all of: #{support}"
|
38
|
+
end
|
39
|
+
|
40
|
+
return schema.mod
|
41
|
+
end
|
42
|
+
|
43
|
+
module Schema
|
44
|
+
class Loaded
|
45
|
+
attr_reader :source, :mod
|
46
|
+
|
47
|
+
def initialize source, mod
|
48
|
+
@source = source
|
49
|
+
@mod = mod
|
50
|
+
end
|
51
|
+
|
52
|
+
def support? support
|
53
|
+
support.each do |m|
|
54
|
+
return false unless @mod.respond_to? m
|
55
|
+
end
|
56
|
+
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.loaded
|
62
|
+
@@loaded ||= {}
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.discovered
|
66
|
+
@@discovered ||= {}
|
67
|
+
end
|
68
|
+
|
69
|
+
module ClassMethods
|
70
|
+
def register_schema name, content_type, impl
|
71
|
+
key = [name, content_type]
|
72
|
+
FFWD::Schema.discovered[key] = impl
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.included mod
|
77
|
+
mod.extend ClassMethods
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.category
|
81
|
+
'schema'
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.load_discovered source
|
85
|
+
FFWD::Schema.discovered.each do |key, mod|
|
86
|
+
FFWD::Schema.loaded[key] = Loaded.new source, mod
|
87
|
+
end
|
88
|
+
|
89
|
+
FFWD::Schema.discovered.clear
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|