wolas_channel 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c8f4ebe2708a9d2083113f4f0013c7d0afaf3693
4
+ data.tar.gz: 90712491e1867b08e2f0661963be3c803aef08f1
5
+ SHA512:
6
+ metadata.gz: 62ddafe52275d7ba7a1013edcd4df50041a586dd2f7cbff4c723d85dd7197ccb7dfd005bb6fb4b2004f643262f61436d19ea79cd265093471e655341572c994d
7
+ data.tar.gz: f2e309aafaf0c587a0fcefd6631c3953970a02ac73bbe767c055dc5ba6529d6e088cfb948f8ed621af45121210daa7ddc24eef89dff98d8149bbafa13c89f890
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in channel.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # WOLAS Channel Service
2
+
3
+ The WOLAS Channel Service creates, stores and updates channel entries for each tenant. As the table structure of this service is a key-value store, each entry into a channel can have unique fields.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'wolas_channel'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install wolas_channel
20
+
21
+ ## Service Overview
22
+
23
+ ### Data Structure
24
+
25
+ The services implements only two tables for all tenants. These tables are in their own schema and are structured as follows.
26
+
27
+ **Channels Table**
28
+
29
+ | id | channel_id | tenant_id | type |
30
+ | --- | --- | --- | --- |
31
+ | 1 | 78ceb188-0090-4877-af5b-c01f2a222966 | 1 | Communication |
32
+ | 2 | f2531651-8a74-4c2c-b655-fd8281165cc0 | 2 | Logs |
33
+ | 3 | 1e972a64-1f88-4158-891a-3483c4fbe4d9 | 3 | Chat Room |
34
+
35
+ **Data Table**
36
+
37
+ | id | channel_id | entity_id | key | value |
38
+ | --- | --- | --- | --- | --- |
39
+ | 1 | 78ceb188-0090-4877-af5b-c01f2a222966 | 06f26d46-73ef-4c0e-9d8a-013f8862a81a | author | John |
40
+ | 2 | 78ceb188-0090-4877-af5b-c01f2a222966 | 06f26d46-73ef-4c0e-9d8a-013f8862a81a | message | This is a message for Banjo |
41
+ | 3 | 78ceb188-0090-4877-af5b-c01f2a222966 | 1493437a-d12b-4832-a715-b475c8b4ddd1 | author | Banjo |
42
+ | 4 | 78ceb188-0090-4877-af5b-c01f2a222966 | 1493437a-d12b-4832-a715-b475c8b4ddd1 | message | Thanks for the message John |
43
+
44
+ ### Permissions
45
+
46
+ Each tenant only has access to channels to which they belong, as defined in the Channels table.
47
+
48
+ If a tenant tries to access a channel to which they do not belong, the service throws a PermissionFailure error.
49
+
50
+ ### Methods
51
+
52
+ #### Instantiate Gem
53
+ ```ruby
54
+ require 'wolas_channel'
55
+ channel = WolasChannel.new(tenant_id)
56
+ ```
57
+
58
+ #### Add Channel
59
+
60
+ The *add_channel* method allows a tenant to create a new channel for their tenancy. This method creates a single entry in the channels table.
61
+
62
+ ```ruby
63
+ args = ('Name for new channel')
64
+
65
+ channel.add_channel('Activities')
66
+ => { channel_created: '92f02e83-b7d4-460c-90bd-f2f2b0a32a6e', channel_type: 'Activities' }
67
+ ```
68
+
69
+ #### Get Entity
70
+
71
+ The *get_entity* method allows a tenant to return an entry from a channel, as long as they are the owner of the channel to which the entry belongs.
72
+ ```ruby
73
+ args = ('entity_id')
74
+
75
+ channel.get_entity('06f26d46-73ef-4c0e-9d8a-013f8862a81a')
76
+ => {
77
+ author: 'John',
78
+ message: 'This is a message for Banjo',
79
+ entity_id: '06f26d46-73ef-4c0e-9d8a-013f8862a81a',
80
+ channel_id: '78ceb188-0090-4877-af5b-c01f2a222966',
81
+ type: 'Communication'
82
+ }
83
+ ```
84
+
85
+ #### Get Channel
86
+
87
+ The *get_channel* method allows a tenant to return all entries from a channel, as long as they are the owner of the channel.
88
+ ```ruby
89
+ args = ('channel_id')
90
+
91
+ channel.get_channel('78ceb188-0090-4877-af5b-c01f2a222966')
92
+ => [
93
+ {
94
+ author: 'John',
95
+ message: 'This is a message for Banjo',
96
+ entity_id: '06f26d46-73ef-4c0e-9d8a-013f8862a81a',
97
+ channel_id: '78ceb188-0090-4877-af5b-c01f2a222966',
98
+ type: 'Communication'
99
+ },
100
+ {
101
+ author: 'Banjo',
102
+ message: 'Thanks for the message John',
103
+ entity_id: '1493437a-d12b-4832-a715-b475c8b4ddd1',
104
+ channel_id: '78ceb188-0090-4877-af5b-c01f2a222966',
105
+ type: 'Communication'
106
+ },
107
+ ]
108
+ ```
109
+
110
+ #### Insert Entity
111
+
112
+ The *insert_entity* method allows a tenant to the create a new entry against a channel, as long as they are the owner of the channel to which they are trying to post the entry.
113
+
114
+ ```ruby
115
+ args = ('entity_id', 'key', 'value')
116
+
117
+ object = { hello: 'world' }
118
+ channel.insert_entity('78ceb188-0090-4877-af5b-c01f2a222966', object)
119
+ =>
120
+ {
121
+ entity_id: '06f26d46-73ef-4c0e-9d8a-013f8862a81a',
122
+ insert: 'SUCCESS'
123
+ }
124
+ ```
125
+
126
+ #### Update Entity
127
+
128
+ The *update_entity* method allows a tenant to the create update an entry in a channel, as long as they are the owner of the channel to which the entry belongs.
129
+
130
+ This method accepts a key and value for the entry to be updated. If the entry already has a matching key to that of the update request, the value of the key is updated in the table. If however the key does not already exist for the entry, it is created instead.
131
+
132
+ ```ruby
133
+ args = ('channel_id', 'object_to_insert')
134
+
135
+ # Existing entity
136
+ {
137
+ author: 'John',
138
+ message: 'This is a message for Banjo',
139
+ entity_id: '06f26d46-73ef-4c0e-9d8a-013f8862a81a',
140
+ channel_id: '78ceb188-0090-4877-af5b-c01f2a222966',
141
+ type: 'Communication'
142
+ }
143
+
144
+ # Updating an existing key
145
+ channel.update_entity('06f26d46-73ef-4c0e-9d8a-013f8862a81a', 'author', 'Sarah')
146
+ => {
147
+ author: 'Sarah',
148
+ message: 'This is a message for Banjo',
149
+ entity_id: '06f26d46-73ef-4c0e-9d8a-013f8862a81a',
150
+ channel_id: '78ceb188-0090-4877-af5b-c01f2a222966',
151
+ type: 'Communication'
152
+ }
153
+
154
+ # Adding a new key
155
+ channel.update_entity('06f26d46-73ef-4c0e-9d8a-013f8862a81a', 'field', 'oval')
156
+ => {
157
+ author: 'Sarah',
158
+ message: 'This is a message for Banjo',
159
+ field: 'oval'
160
+ entity_id: '06f26d46-73ef-4c0e-9d8a-013f8862a81a',
161
+ channel_id: '78ceb188-0090-4877-af5b-c01f2a222966',
162
+ type: 'Communication'
163
+ }
164
+ ```
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "wolas_channel"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/lib/connection.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'mysql2'
2
+ # Establish connection to mysql2
3
+ class Connection
4
+ def open
5
+ @connection = Mysql2::Client.new(
6
+ :host => '127.0.0.1',
7
+ :username => "root",
8
+ :connect_timeout => 20
9
+ )
10
+ end
11
+
12
+ def prepare(code)
13
+ result = @connection.prepare(code)
14
+ return result
15
+ end
16
+
17
+ def close
18
+ @connection.close
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ class WolasChannel
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,258 @@
1
+ require 'wolas_channel/version'
2
+ require 'connection'
3
+ require 'byebug'
4
+ require 'securerandom'
5
+
6
+ # This module contains all the error handling classes
7
+ module ErrorHandling
8
+ # This error is raised when a tenant requests a channel that does not belong
9
+ # to their tenant id
10
+ class PermissionFailure < StandardError; end
11
+ end
12
+
13
+ # This class handles mapping the channel items from and to the database
14
+ class WolasChannel
15
+ # Initialize a tenant_id when the channel service is instantiated
16
+ def initialize(tenant_id)
17
+ @tenant_id = tenant_id
18
+ end
19
+
20
+ # This method allows a tenant to add a channel to their tenancy
21
+ def add_channel(type = 'Not Specified')
22
+ begin
23
+ # Connect to the database
24
+ connect = Connection.new.open
25
+ # Generate a UUID for the new channel
26
+ channel_id = SecureRandom.uuid
27
+
28
+ insert = "INSERT INTO `channel`.`channels` (`channel_id`, `tenant_id`, `type`) VALUES (?, ?, ?)"
29
+ # This method performs the insert. Mysql2 does not return an object on a successful
30
+ # insert
31
+ prepare_exe(connect, insert, channel_id, @tenant_id, type)
32
+
33
+ return { channel_created: channel_id, channel_type: type }
34
+ ensure
35
+ connect.close
36
+ end
37
+ end
38
+
39
+ # This method returns an entity based off an id. If the channel id of the
40
+ # entity does not match that of the tenant making the request, an error is raised
41
+ def get_entity(entity_id)
42
+ begin
43
+ # Connect to the database. Instance variable used to pass the connection to
44
+ # private methods
45
+ @connect = Connection.new.open
46
+ # Determine if the tenant is able to view this entity based off the
47
+ # channel and return the type of the entity
48
+ check = tenant_entity(entity_id)
49
+ type = check[0]
50
+ return nil if check == [nil, nil]
51
+
52
+ # Now the entity is requested
53
+ entity = entity_construct(entity_id, type)
54
+ return entity
55
+ ensure
56
+ @connect.close
57
+ end
58
+ end
59
+
60
+ # This method returns all entries in a channel.
61
+ # If the channel id of the entity returned does not match that of the tenant making
62
+ # the request, an error is raised
63
+ def get_channel(channel_id)
64
+ begin
65
+ # Connect to the database. Instance variable used to pass connection to
66
+ # private methods
67
+ @connect = Connection.new.open
68
+ # Determine if this channel is valid for the tenant and returns its type
69
+ type = tenant_channel(channel_id)
70
+ return nil if type == nil
71
+
72
+ # Now the entities are requested
73
+ ent_q = "SELECT * FROM `channel`.`data` WHERE `data`.`channel_id` = ?"
74
+ ent = prepare_exe(@connect, ent_q, channel_id)
75
+
76
+ # Return nil if nothing is returned from the select
77
+ return nil if ent.count == 0
78
+
79
+ # Construct the object requested using the generic method
80
+ entitys = object_constructor(ent)
81
+
82
+ # Apply the type to each object constructed
83
+ entitys.each do |ents|
84
+ ents[:type] = type
85
+ end
86
+ return entitys
87
+ ensure
88
+ @connect.close
89
+ end
90
+ end
91
+
92
+ # This method allows the creation of a new entity against a channel. The tenant
93
+ # id must match the id of the channel passed or an error is raised
94
+ def insert_entity(channel_id, obj)
95
+ begin
96
+ # Connect to the database. Instance variable used to pass connection to
97
+ # private methods
98
+ @connect = Connection.new.open
99
+ # Determine if the tenant has access to the channel requested
100
+ tenant_channel(channel_id)
101
+
102
+ # Assign a UUID for the new entity
103
+ entity_id = SecureRandom.uuid
104
+ # Now we construct the key / value insert for each pair in the object
105
+ # passed to the method
106
+ obj.each do |key, value|
107
+ insert = "INSERT INTO `channel`.`data` (`channel_id`, `key`, `value`, `entity_id`)\
108
+ VALUES (?, ?, ?, ?)"
109
+ # This method performs the insert. Mysql2 does not return an object on a successful
110
+ # insert
111
+ prepare_exe(@connect, insert, channel_id, key.to_s, value, entity_id)
112
+ end
113
+ return { entity_id: entity_id, insert: 'SUCCESS' }
114
+ ensure
115
+ @connect.close
116
+ end
117
+ end
118
+
119
+ # This method allows the update of an entity
120
+ def update_entity(entity_id, key, value)
121
+ begin
122
+ # Connect to the database. Instance variable used to pass connection to
123
+ # private methods
124
+ @connect = Connection.new.open
125
+ # Determine if the tenant has access to the entity requested
126
+ check = tenant_entity(entity_id)
127
+ type = check[0]
128
+ channel_id = check[1]
129
+
130
+ # Lookup the key to be changed on the entity, insert it if it doesn't exist
131
+ ent_q = "SELECT * FROM `channel`.`data` WHERE `data`.`entity_id` = ? AND `data`.`key` = ?"
132
+ ent = prepare_exe(@connect, ent_q, entity_id, key)
133
+
134
+ if ent.count != 0
135
+ # update the existing key value
136
+ update = "UPDATE `channel`.`data` SET `data`.`value`= ? \
137
+ WHERE `data`.`entity_id` = ? AND `data`.`key` = ?"
138
+ prepare_exe(@connect, update, value, entity_id, key.to_s)
139
+
140
+ # Now that the insert has been completed, the entire object is returned
141
+ # from the database and constructed to return to the user
142
+ entity = entity_construct(entity_id, type)
143
+ else
144
+ # insert the new key as it does not exist for the entity
145
+ insert = "INSERT INTO `channel`.`data` (`channel_id`, `key`, `value`, `entity_id`)\
146
+ VALUES (?, ?, ?, ?)"
147
+ prepare_exe(@connect, insert, key.to_s, value, entity_id)
148
+
149
+ # Now that the insert has been completed, the entire object is returned
150
+ # from the database and constructed to return to the user
151
+ entity = entity_construct(entity_id, type)
152
+ end
153
+
154
+ return entity
155
+ ensure
156
+ @connect.close
157
+ end
158
+ end
159
+
160
+ private
161
+ # This method constructs a single object from key-values returned from a
162
+ # query
163
+ def object_constructor(ent)
164
+ # ent is a mysql2 object. This generic map returns an array of the rows
165
+ entity_raw = ent.map{ |e| e }
166
+ entity = []
167
+ @init = false
168
+ entity_h = {}
169
+ # This method identifies when the entity id changes, pushes the built hash
170
+ # to the array and starts a new hash for the new entity id
171
+ entity_raw.each do |hash|
172
+ if hash['entity_id'] != @init && @init != false
173
+ entity_h[:channel_id] = @channel_id
174
+ entity_h[:entity_id] = @init
175
+ entity << entity_h
176
+ entity_h = {}
177
+ end
178
+ entity_h[hash['key'].to_sym] = hash['value']
179
+ # Init is used to determine when to push to the array
180
+ @init = hash['entity_id']
181
+ # The resource_id for the current hash is stored and added to the entity
182
+ # hash before it is finalised and pushed to an array
183
+ @channel_id = hash['channel_id']
184
+ end
185
+ # Push the last built hash to the array
186
+ entity_h[:channel_id] = @channel_id
187
+ entity_h[:entity_id] = @init
188
+ entity << entity_h
189
+ return entity
190
+ end
191
+
192
+ # This method determines the if the channel on an entity is availble to the tenant
193
+ # and returns the type of channel
194
+ def tenant_entity(entity_id)
195
+ res_q = "SELECT * FROM `channel`.`channels` \
196
+ INNER JOIN `channel`.`data` ON `channels`.`channel_id` = `data`.`channel_id` \
197
+ WHERE `data`.`entity_id` = ?"
198
+ res = prepare_exe(@connect, res_q, entity_id)
199
+ return [nil, nil] if res.count == 0
200
+
201
+ # Check the tenant_id on each row returned. They should all be the same as they
202
+ # will all join to the same row on the channels table
203
+ channels = res.map{ |a| a }
204
+ channels.each do |check|
205
+ unless check['tenant_id'] == @tenant_id
206
+ raise ErrorHandling::PermissionFailure, 'You do not have permission to access information for this tenant'
207
+ end
208
+ @type = check['type']
209
+ @channel_id = check['channel_id']
210
+ end
211
+ return @type, @channel_id
212
+ end
213
+
214
+ # This method determines if the channel requested is available to the tenant
215
+ # and returns its type
216
+ def tenant_channel(channel_id)
217
+ res_q = "SELECT * FROM `channel`.`channels` WHERE `channels`.`channel_id` = ?"
218
+ res = prepare_exe(@connect, res_q, channel_id)
219
+ return nil if res.count == 0
220
+
221
+ # Check the tenant_id on each row returned. They should all be the same, and in this
222
+ # instance their should only be one row.
223
+ channels = res.map{ |a| a }
224
+ channels.each do |check|
225
+ unless check['tenant_id'] == @tenant_id
226
+ raise ErrorHandling::PermissionFailure, 'You do not have permission to access information for this tenant'
227
+ end
228
+ @type = check['type']
229
+ @channel_id = check['channel_id']
230
+ end
231
+ return @type
232
+ end
233
+
234
+ # This method returns a constructed entity from an entity id
235
+ def entity_construct(entity_id, type)
236
+ ent_q = "SELECT * FROM `channel`.`data` WHERE `entity_id` = ?"
237
+ ent = prepare_exe(@connect, ent_q, entity_id)
238
+
239
+ # Return nil if nothing is returned from the select
240
+ return nil if ent.count == 0
241
+
242
+ # Construct the object requested using the generic method.
243
+ entity = object_constructor(ent)[0]
244
+
245
+ # Apply the type to the entity
246
+ entity[:type] = type
247
+ return entity
248
+ end
249
+
250
+ # This method prepares and executes code from the two methods defined by the
251
+ # connection call
252
+ def prepare_exe(connection, statement, *inputs)
253
+ sqlobj = connection.prepare(statement)
254
+ result = sqlobj.execute(*inputs)
255
+ return result
256
+ end
257
+ end
258
+
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'wolas_channel/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'wolas_channel'
8
+ spec.version = WolasChannel::VERSION
9
+ spec.authors = ['WOLAS']
10
+ spec.email = ['sjeston@wolas.com.au']
11
+
12
+ spec.summary = 'WOLAS Channel Service'
13
+ spec.description = 'Enables key-value storage of channel items (communication, change logs etc)'
14
+ spec.homepage = 'https://bitbucket.org/wolas-revolution/srvc-channel'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.8'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rspec'
24
+ spec.add_development_dependency 'byebug'
25
+ spec.add_development_dependency 'mysql2'
26
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wolas_channel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - WOLAS
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mysql2
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Enables key-value storage of channel items (communication, change logs
84
+ etc)
85
+ email:
86
+ - sjeston@wolas.com.au
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".travis.yml"
94
+ - Gemfile
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - lib/connection.rb
100
+ - lib/wolas_channel.rb
101
+ - lib/wolas_channel/version.rb
102
+ - wolas_channel.gemspec
103
+ homepage: https://bitbucket.org/wolas-revolution/srvc-channel
104
+ licenses: []
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.4.5
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: WOLAS Channel Service
126
+ test_files: []