connfu-client 0.1

Sign up to get free protection for your applications and to get access to all the features.
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