connfu-client 0.1

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.
Files changed (94) hide show
  1. data/.rspec +3 -0
  2. data/.rvmrc +2 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +120 -0
  5. data/LICENSE.txt +661 -0
  6. data/README.rdoc +398 -0
  7. data/Rakefile +38 -0
  8. data/bin/.DS_Store +0 -0
  9. data/bin/connfu-client +94 -0
  10. data/connfu-client.gemspec +41 -0
  11. data/examples/conference/application.rb +68 -0
  12. data/examples/conference/conference.rb +33 -0
  13. data/examples/conference/conference_app.rb +25 -0
  14. data/examples/conference/connfu.log +5 -0
  15. data/examples/conference/wall.rb +11 -0
  16. data/examples/provisioning/app/get.rb +29 -0
  17. data/examples/provisioning/channels/get.rb +34 -0
  18. data/examples/provisioning/rss/create.rb +28 -0
  19. data/examples/provisioning/rss/delete.rb +27 -0
  20. data/examples/provisioning/rss/get.rb +32 -0
  21. data/examples/provisioning/rss/put.rb +29 -0
  22. data/examples/provisioning/setup.rb +2 -0
  23. data/examples/provisioning/twitter/create.rb +31 -0
  24. data/examples/provisioning/twitter/delete.rb +27 -0
  25. data/examples/provisioning/twitter/get.rb +32 -0
  26. data/examples/provisioning/twitter/put.rb +29 -0
  27. data/examples/provisioning/voice/create.rb +26 -0
  28. data/examples/provisioning/voice/delete.rb +27 -0
  29. data/examples/provisioning/voice/get.rb +36 -0
  30. data/examples/provisioning/voice/phones/create.rb +26 -0
  31. data/examples/provisioning/voice/phones/delete.rb +28 -0
  32. data/examples/provisioning/voice/phones/get.rb +38 -0
  33. data/examples/provisioning/voice/put.rb +38 -0
  34. data/examples/provisioning/voice/whitelist/create.rb +26 -0
  35. data/examples/provisioning/voice/whitelist/delete.rb +27 -0
  36. data/examples/provisioning/voice/whitelist/get.rb +36 -0
  37. data/examples/provisioning/voice/whitelist/put.rb +27 -0
  38. data/lib/connfu.rb +134 -0
  39. data/lib/connfu/cli/generator.rb +71 -0
  40. data/lib/connfu/connfu_logger.rb +88 -0
  41. data/lib/connfu/connfu_message_formatter.rb +134 -0
  42. data/lib/connfu/connfu_stream.rb +182 -0
  43. data/lib/connfu/dispatcher.rb +164 -0
  44. data/lib/connfu/dsl.rb +84 -0
  45. data/lib/connfu/events.rb +32 -0
  46. data/lib/connfu/listener.rb +85 -0
  47. data/lib/connfu/listener_channel.rb +100 -0
  48. data/lib/connfu/message.rb +74 -0
  49. data/lib/connfu/provisioning.rb +12 -0
  50. data/lib/connfu/provisioning/application.rb +374 -0
  51. data/lib/connfu/provisioning/base.rb +95 -0
  52. data/lib/connfu/provisioning/channel.rb +79 -0
  53. data/lib/connfu/provisioning/phone.rb +55 -0
  54. data/lib/connfu/provisioning/rss.rb +21 -0
  55. data/lib/connfu/provisioning/twitter.rb +28 -0
  56. data/lib/connfu/provisioning/voice.rb +89 -0
  57. data/lib/connfu/provisioning/whitelist.rb +62 -0
  58. data/lib/connfu/version.rb +6 -0
  59. data/lib/rdoc/generator/template/connfu/_context.rhtml +209 -0
  60. data/lib/rdoc/generator/template/connfu/_head.rhtml +7 -0
  61. data/lib/rdoc/generator/template/connfu/class.rhtml +38 -0
  62. data/lib/rdoc/generator/template/connfu/file.rhtml +36 -0
  63. data/lib/rdoc/generator/template/connfu/index.rhtml +13 -0
  64. data/lib/rdoc/generator/template/connfu/resources/apple-touch-icon.png +0 -0
  65. data/lib/rdoc/generator/template/connfu/resources/css/github.css +129 -0
  66. data/lib/rdoc/generator/template/connfu/resources/css/main.css +339 -0
  67. data/lib/rdoc/generator/template/connfu/resources/css/panel.css +389 -0
  68. data/lib/rdoc/generator/template/connfu/resources/css/reset.css +53 -0
  69. data/lib/rdoc/generator/template/connfu/resources/favicon.ico +0 -0
  70. data/lib/rdoc/generator/template/connfu/resources/i/arrows.png +0 -0
  71. data/lib/rdoc/generator/template/connfu/resources/i/results_bg.png +0 -0
  72. data/lib/rdoc/generator/template/connfu/resources/i/tree_bg.png +0 -0
  73. data/lib/rdoc/generator/template/connfu/resources/js/highlight.pack.js +1 -0
  74. data/lib/rdoc/generator/template/connfu/resources/js/jquery-1.3.2.min.js +19 -0
  75. data/lib/rdoc/generator/template/connfu/resources/js/jquery-effect.js +593 -0
  76. data/lib/rdoc/generator/template/connfu/resources/js/main.js +20 -0
  77. data/lib/rdoc/generator/template/connfu/resources/js/searchdoc.js +628 -0
  78. data/lib/rdoc/generator/template/connfu/resources/panel/index.html +72 -0
  79. data/lib/rdoc/generator/template/connfu/se_index.rhtml +8 -0
  80. data/spec/connfu_message_formatter_spec.rb +88 -0
  81. data/spec/connfu_spec.rb +51 -0
  82. data/spec/connfu_stream_spec.rb +84 -0
  83. data/spec/dispatcher_spec.rb +227 -0
  84. data/spec/dsl_spec.rb +159 -0
  85. data/spec/listener_channel_spec.rb +130 -0
  86. data/spec/listener_spec.rb +67 -0
  87. data/spec/provisioning/application_spec.rb +47 -0
  88. data/spec/provisioning/channel_shared_examples.rb +52 -0
  89. data/spec/provisioning/channel_spec.rb +13 -0
  90. data/spec/provisioning/phone_spec.rb +88 -0
  91. data/spec/provisioning/voice_spec.rb +138 -0
  92. data/spec/provisioning_spec.rb +500 -0
  93. data/spec/spec_helper.rb +51 -0
  94. metadata +298 -0
@@ -0,0 +1,164 @@
1
+ require 'connfu/connfu_logger'
2
+
3
+ module Connfu
4
+
5
+ ##
6
+ # Class that dispatches the external events.
7
+ # Currently there is no thread pool, so each event is executed sequentially
8
+ class Dispatcher
9
+ include Connfu::ConnfuLogger
10
+
11
+ attr_reader :continue
12
+ attr_reader :counter
13
+ attr_accessor :max_messages
14
+
15
+ ##
16
+ # Initializer
17
+ #
18
+ # ==== Parameters
19
+ # * +queue+ Connfu::Events instance to wait for incoming Message events
20
+ # * +listener_channels+ Hash of listener_channels.
21
+ # * :key => channel name (valid ListenerChannel::CHANNEL_TYPES)
22
+ # * :value => ListenerChannel instance
23
+ # * +app_channels+ information about application channels to set the channel_name associated to an inbound event
24
+ def initialize(queue, listener_channels, app_channels = [])
25
+ if listener_channels.nil? or !listener_channels.is_a?(Hash) or listener_channels.length == 0
26
+ raise Exception, "Unable to dispatch events if no channel is defined"
27
+ end
28
+
29
+ @continue = true
30
+ @counter = 0
31
+ @max_messages = 0
32
+ @listener_channels = listener_channels
33
+ @app_channels = app_channels
34
+ logger.debug("Dispatcher initializer")
35
+ @queue = queue
36
+ end
37
+
38
+ ##
39
+ # start waiting for incoming Message.
40
+ # Should create a new thread and wait to new events to come
41
+ # ==== Parameters
42
+ # * +queue+ optional Connfu::Events instance to wait for incoming Message events. If nil, the value got in the initializer is used
43
+ def start(queue = nil)
44
+ queue.nil? and queue = @queue
45
+ logger.debug("Dispatcher starts")
46
+ @thread = Thread.new {
47
+ while continue
48
+ logger.debug("Dispatcher waiting for a message from the Listener")
49
+ event = queue.get
50
+ @counter = @counter + 1
51
+ logger.debug "---------------------------------"
52
+ logger.debug "#{self.class} => #{event}"
53
+
54
+ if event.is_a?(Array)
55
+ event.each { |ev|
56
+ set_channels!(ev)
57
+ process_message(ev)
58
+ }
59
+ else
60
+ set_channels!(event)
61
+ process_message(event)
62
+ end
63
+
64
+ end
65
+ }
66
+ end
67
+
68
+ ##
69
+ # This method should be called for the thread that started the dispatcher
70
+ # in order to wait for dispatching incoming events from the listener
71
+ def join
72
+ @thread.join
73
+ end
74
+
75
+ ##
76
+ # Stop waiting for incoming events
77
+ def stop
78
+ @continue = false
79
+ end
80
+
81
+ private
82
+
83
+ ##
84
+ # Helper to validate if dispatcher should wait for new messages
85
+ def continue
86
+ if max_messages > 0
87
+ @continue and @counter < max_messages
88
+ else
89
+ @continue
90
+ end
91
+ end
92
+
93
+ ##
94
+ # Sets the message channel_name attribute.
95
+ # The result is a list of application channels that should be advised about
96
+ # the inbound message
97
+ #
98
+ # * if message["type"].eql?("twitter"): message["channel_type"] is an
99
+ # array of all the application twitter channels that has associated the message twitter account.
100
+ # i.e.
101
+ # Application channels:
102
+ # @app_channels = [
103
+ # {"uid"=>"twitter-channel-1", "type"=>"twitter", "accounts"=>[{"name"=>"juandebravo"}, {"name"=>"connfudev"}]},
104
+ # {"uid"=>"twitter-channel-2", "type"=>"twitter", "accounts"=>[{"name"=>"telefonicaid"}]}]
105
+ # Incoming message:
106
+ # message.channel_type = "twitter"
107
+ # message.from = "juandebravo"
108
+ #
109
+ # set_channels!(message) => message.channel_name = ["twitter-channel-1"]
110
+ #
111
+ # ==== Parameters
112
+ # * +message+ Connfu::Message with no channel_name info
113
+ # ==== Return
114
+ # * Connfu::Message with the channel_name filled with the relevant app channels
115
+ def set_channels!(message) # :doc:
116
+ channel_type = message.channel_type
117
+
118
+ # select channels with the same channel_type as the incoming message
119
+ channels = @app_channels.select { |channel| channel["type"].eql?(channel_type) }
120
+
121
+ # filter channels
122
+ case message.channel_type
123
+ when "twitter" # filter by from or to account
124
+ channels = channels.select { |channel|
125
+ channel["accounts"].select { |item|
126
+ item["name"].eql?(message.from) or item["name"].eql?(message.to)
127
+ }.length > 0
128
+ }
129
+ when "voice" # filter by did
130
+ channels = channels.select { |channel|
131
+ channel["uid"].eql?(message.channel_name)
132
+ #channel["phones"].select{|item|
133
+ # item["phone_number"].eql?(message.to)
134
+ #}.length > 0
135
+ }
136
+ when "sms"
137
+ channels = channels.select { |channel|
138
+ channel["phones"].select{|item|
139
+ item["phone_number"].eql?(message.to)
140
+ }.length > 0
141
+ }
142
+ else
143
+ logger.warn("This code should not be executed because the first select should avoid this")
144
+ logger.info("Unexpected message: #{message.channel_type}")
145
+ channels = []
146
+ end
147
+
148
+ # get only the channel unique identifier
149
+ channels = channels.map { |channel| channel["uid"] }
150
+
151
+ logger.debug "Setting channels in the incoming message to #{channels}"
152
+ message.channel_name = channels
153
+ end
154
+
155
+ # Executes the blocks that are associated to that channel and event type
156
+ # @param *message* incoming message to be processed
157
+ def process_message(message)
158
+ logger.info("Calling event #{message.message_type} in the channel #{message.channel_type}")
159
+ @listener_channels[message.channel_type.to_sym].message(message.message_type.to_sym, message)
160
+ end
161
+
162
+ end
163
+
164
+ end
data/lib/connfu/dsl.rb ADDED
@@ -0,0 +1,84 @@
1
+ require 'connfu/connfu_logger'
2
+
3
+ module Connfu
4
+ ##
5
+ # This class parses a connFu application defined using the DSL
6
+ # i.e.
7
+ #
8
+ # Connfu.application(VALID_TOKEN, ENDPOINT) {
9
+ # listen(:voice) do |conference| {
10
+ # conference.on(:join) do |call|
11
+ # puts "New inbound call from #{call[:from]} on number #{call[:to]}"
12
+ # end
13
+ # }
14
+ # listen(:twitter) do |conference| {
15
+ # twitter.on(:new) do |tweet|
16
+ # puts "#{tweet[:channel_name]} just posted a new tweet: #{tweet.content}"
17
+ # end
18
+ # }
19
+ # }
20
+ #
21
+ module DSL
22
+ include Connfu::ConnfuLogger
23
+
24
+ class << self
25
+
26
+ ##
27
+ # Parses the given block
28
+ # ==== Return
29
+ #
30
+ # Hash of values containing as key the listener channel type and as value the blocks to be executed
31
+ # when an event associated to that channel is received
32
+ def run
33
+ @listener_channels = {}
34
+ if block_given?
35
+ self.instance_exec(&Proc.new)
36
+ @listener_channels
37
+ else
38
+ nil
39
+ end
40
+ end
41
+
42
+ ##
43
+ # This method is called while parsing the application logic and a new channel is defined.
44
+ # It inserts a new ListenerChannels in the listener_channels attribute with the Proc associated to
45
+ # each event type.
46
+ #
47
+ # i.e.
48
+ # listen(:voice) do |conference| {
49
+ # conference.on(:join) do |call|
50
+ # puts "New inbound call from #{call[:from]} on number #{call[:to]}"
51
+ # end
52
+ # }
53
+ #
54
+ # While parsing this snippet of code, a new listener_channel will be stored, indicating that when
55
+ # a new +join+ event associated to a +voice+ channel is received, the line
56
+ #
57
+ # puts "New inbound call from #{call[:from]} on number #{call[:to]}"
58
+ #
59
+ # must be executed.
60
+ #
61
+ # ==== Parameters
62
+ #
63
+ # * +name+ channel name
64
+ # * +args+ actually not in used
65
+ def listen(name, args = {})
66
+
67
+ unless ListenerChannel.valid?(name)
68
+ logger.error "Invalid channel name #{name}"
69
+ return nil
70
+ end
71
+
72
+ # Create a new channel listener
73
+ listener_channel = Connfu::ListenerChannel.new(name)
74
+
75
+ # If defined, insert into the channel the desired logic
76
+ block_given? and listener_channel.add_block(&Proc.new)
77
+
78
+ # Add channel to channels array
79
+ @listener_channels.store(name, listener_channel)
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,32 @@
1
+ module Connfu
2
+
3
+ ##
4
+ # This class is used to send Message instances between transport (listener) and dispatcher layers
5
+ class Events
6
+
7
+ ##
8
+ # Initializer
9
+ def initialize
10
+ @queue = Queue.new
11
+ end
12
+
13
+ ##
14
+ # Returns a message from the queue. It waits until there's at least one message in the queue and acts
15
+ # as a FIFO queue.
16
+ #
17
+ # ==== Returns
18
+ # Connfu::Message instance
19
+ def get
20
+ @queue.pop
21
+ end
22
+
23
+ ##
24
+ # Inserts a message in the queue
25
+ #
26
+ # ==== Parameters
27
+ # * +event+ Connfu::Message instance
28
+ def put(event)
29
+ @queue << event
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,85 @@
1
+ require 'connfu/connfu_stream'
2
+ require 'connfu/connfu_logger'
3
+
4
+ module Connfu
5
+
6
+ ##
7
+ # Class that listen to external events.
8
+ # Delegates the behavior in ConnfuStream so any new external resource
9
+ # can be added and no need to change the interface is required
10
+ #
11
+ class Listener
12
+ include Connfu::ConnfuLogger # application logger
13
+
14
+ attr_reader :continue
15
+ attr_reader :counter
16
+ attr_reader :app_stream
17
+ attr_accessor :max_messages # max amount of messages to receive
18
+
19
+ ##
20
+ # Listener initializer.
21
+ #
22
+ # ==== Parameters
23
+ # * +queue+ Connfu::Events instance to forward incoming events to be processed by the Dispatcher class
24
+ # * +app_stream+ valid HTTP stream url to connect and listen events
25
+ # * +token+ valid token to get access to a connFu Stream
26
+ # * +stream_endpoint+ endpoint to open a keepalive HTTP connection
27
+ def initialize(queue, app_stream, token, stream_endpoint)
28
+ @connfu_stream = Connfu::ConnfuStream.new(app_stream, token, stream_endpoint)
29
+ @continue = true
30
+ @counter = 0
31
+ @max_messages = 0
32
+ @queue = queue
33
+ end
34
+
35
+ ##
36
+ # start listening.
37
+ # Should create a new thread and wait to new events to come
38
+ # ==== Parameters
39
+ # * +queue+ to send incoming events to the Dispatcher class
40
+ def start(queue = nil)
41
+ queue.nil? and queue = @queue
42
+ logger.debug "Listener starts..."
43
+
44
+ @thread = Thread.new {
45
+ # listen to connFu
46
+ @connfu_stream.start_listening
47
+ }
48
+
49
+ while continue do
50
+ logger.debug "Waiting for a message from connFu stream"
51
+ message = @connfu_stream.get
52
+ @counter = @counter + 1
53
+
54
+ logger.debug "#{self.class} got message => #{message}"
55
+ # Write in internal Queue
56
+ queue.put(message)
57
+ end
58
+
59
+ end
60
+
61
+ ##
62
+ # Wait to get external events
63
+ def join
64
+ @thread.join
65
+ end
66
+
67
+ ##
68
+ # stop waiting for external events
69
+ def stop
70
+ @continue = false
71
+ end
72
+
73
+ ##
74
+ # checks if the thread should continue listening or return
75
+ def continue
76
+ if max_messages > 0
77
+ @continue and @counter < max_messages
78
+ else
79
+ @continue
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,100 @@
1
+ require 'connfu/connfu_logger'
2
+
3
+ module Connfu
4
+
5
+ ##
6
+ # This class models a specific connFu listener channel.
7
+ # A connFu listener channel is the path to fetch events from your resources.
8
+ #
9
+ # ==== Current supported channel types:
10
+ # - +:voice+: inbound calls to a phone number mapped to the application
11
+ # - +:sms+: inbound sms to a phone number mapped to the application
12
+ # - +:twitter+: tweet messages that matches at least one of the rules defined by the application
13
+ # - +:rss+: atom or rss feed
14
+ class ListenerChannel
15
+ include Connfu::ConnfuLogger # application logger
16
+
17
+ # Constant that defines the supported channels
18
+ CHANNEL_TYPES = [:voice, :twitter, :sms, :rss]
19
+ CHANNEL_TYPES.freeze
20
+ CHANNEL_TYPES.each { |channel| channel.freeze }
21
+
22
+ # Constant that defines the supported channel events
23
+ EVENT_TYPES = [:new, :join, :leave, :new_topic]
24
+ EVENT_TYPES.freeze
25
+ EVENT_TYPES.each { |event| event.freeze }
26
+
27
+ # channel name (a CHANNELS item)
28
+ attr_reader :name
29
+
30
+ # blocks associated to the channel. Hash object (key = event_type, value = Proc)
31
+ attr_accessor :blocks
32
+
33
+ # how to filter inbound events from connFu
34
+ attr_reader :_filter
35
+
36
+ ##
37
+ # Channel initializer
38
+ #
39
+ # ==== Parameters
40
+ # * +name+ channel name (CHANNELS item)
41
+ def initialize(name)
42
+ @name = name
43
+ # initialize an Struct that shall keep the event blocks. There is a specific key per each event type
44
+ @blocks = Struct.new(*EVENT_TYPES).new(*Array.new(EVENT_TYPES.length){Array.new()})
45
+ end
46
+
47
+ ##
48
+ # Method that defines a new event in a channel
49
+ # ==== Parameters
50
+ # * +event+ event type. Must be included in EVENT_TYPES
51
+ #
52
+ def on(event)
53
+ EVENT_TYPES.include?(event) or (self.class.logger.error "Invalid event #{event}" and return)
54
+ block_given? and @blocks[event] << Proc.new
55
+ end
56
+
57
+ ##
58
+ #
59
+ def message(event, arg = nil)
60
+ @blocks[event].each { |block|
61
+ block.call(arg)
62
+ }
63
+ end
64
+
65
+ def filter=(value)
66
+ @_filter = value
67
+ # TODO
68
+ # connFu provisioning request
69
+ end
70
+
71
+ def filter
72
+ @_filter
73
+ end
74
+
75
+ def add_block
76
+ block_given? and self.instance_exec self, &Proc.new
77
+
78
+ #
79
+ # Avoid using instance_exec: we can directly call the Proc received
80
+ # but we need to change the expectations regarding context "messages"
81
+ # @connfu.listener_channels[:foo].should... => @connfu.should
82
+ #
83
+ #block_given? and Proc.new.call(self)
84
+ end
85
+
86
+ class << self
87
+ ##
88
+ # Checks if a channel name is valid
89
+ # ==== Parameters
90
+ # * +name+ channel name
91
+ # ==== Return
92
+ # true if the channel name if valid, false if not
93
+ def valid?(name)
94
+ CHANNEL_TYPES.include?(name)
95
+ end
96
+ end
97
+
98
+ end
99
+
100
+ end