cloud_powers 0.2.3 → 0.2.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 345005638bc5e69081209792b8b9884ba2db2605
4
- data.tar.gz: f5b69e0a4d366da8e6512b2ea6062c18bf17dc34
3
+ metadata.gz: 536b4abf5027aa70d284971a54fa2ec662cc8983
4
+ data.tar.gz: 8bd347c9a99d78f4dce7dec27b837dfa6bd21e22
5
5
  SHA512:
6
- metadata.gz: 09ec8ecacab90dc97ea58cfe3b70c1359d3ed381076403edbca261265bc9eeee71d5e1ace2b5dea55401eb57d2c79c2ea258aef8242846c75ccd57d1eceb2542
7
- data.tar.gz: e4c21cdb59a03d37fd45d4dba3cf8e3b5d6732a3deaa6351ef7c24220035f5cc42c2f78a81fec804abed81f8d211b3d6441caeecc6d26d021fb93479a2a0d8de
6
+ metadata.gz: 78dd9784282f4d316dce04003efb37cf579b3a52f9c2bd5187f92c6c3afde853cfdeb2038e079a489bf1fcd9fa360121c920ba74829575e0a31fddd02ff8dc9d
7
+ data.tar.gz: b59c25e12a62757894c46cc1fdf00be0a3b4ff79b06001f18cd9118af2eaeea4c961e2a646b57841d9007c45918ee91bfeecaaeb6d0b0f40dd3d57a9c00953f2
data/.test.env.example CHANGED
@@ -25,4 +25,4 @@ FINISHED_QUEUE_ADDRESS=""
25
25
 
26
26
 
27
27
  # --THIS SHOULD ALWAYS BE THE LAST LINE-- < delete everything below this line for production
28
- TESTING="true"
28
+ TEST="true"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cloud_powers (0.2.3)
4
+ cloud_powers (0.2.4)
5
5
  activesupport-core-ext (~> 4)
6
6
  aws-sdk (~> 2)
7
7
  dotenv (~> 2.1)
data/cloud_powers.gemspec CHANGED
@@ -11,13 +11,14 @@ Gem::Specification.new do |spec|
11
11
  spec.email = 'adam.phillipps@gmail.com'
12
12
  spec.summary = %q{Cloud providers wrapper. Currently only AWS is supported.}
13
13
  spec.description = <<-EOF
14
- CloudPowers is a wrapper around AWS and other cloud services.
15
- It was developed specifically for the Brain project
16
- but hopefully can be used in any other ruby project that needs to use cloud
17
- service providers' resources. Version 0.2.3 has a little EC2, S3, SQS, SNS
18
- and Kinesis. V-0.2.4 should be out in a day or so and it will include IoT
19
- basics and specs too. This project is actively being developed, so more
20
- additions, specs and docs will be updated frequently. I always welcome input.
14
+ CloudPowers is a wrapper around AWS and in the future, other cloud service Providers.
15
+ It was developed specifically for the Brain project but hopefully can be used
16
+ in any other ruby project that needs to use cloud service providers' resources.
17
+ Version 0.2.3 has a little EC2, S3, SQS, SNS and Kinesis. V 0.2.4 includes
18
+ a Context a Workflow revamp. Websockets will be next.
19
+ This project is actively being developed, so more additions, specs and docs
20
+ will be updated frequently with new funcionality but the gem will follow good
21
+ practices for versioning. I always welcome input.
21
22
  Enjoy!
22
23
  EOF
23
24
  spec.homepage = 'https://github.com/adam-phillipps/cloud_powers'
@@ -0,0 +1,235 @@
1
+ require 'json'
2
+ require_relative 'helper'
3
+
4
+ module Smash
5
+ module CloudPowers
6
+ # The Context class is the class that handles serializing the Context so that it
7
+ # can be passed from node to node and rebuilt. This provides a way for nodes in
8
+ # the same Brain to have an awareness of required communication modes, for example.
9
+ # Because resources generally require some sort of information to connect with
10
+ # or create and a Brain operates in the same Context, it can use the Context to
11
+ # decouple the creation and usage from the coordination.
12
+ class Context
13
+ include Smash::CloudPowers::Helper
14
+
15
+ attr_accessor :package # The given data structure that is used to build @structure
16
+ attr_reader :structure # A Hash that is used to represent the scheme of the Context
17
+
18
+ # Attempts to create a Context out of the argument(s) you've
19
+ # passed.
20
+ # === @params args <Hash|JSON|Array|enumerable list>
21
+ # - exp Hash:
22
+ # `{ task: 'demo', queue: [:backlog, :sned], pipe: :status_stream }`
23
+ # each key is a module or class that is in CloudPowers and each value
24
+ # is either an array of configurations information or a single configuration.
25
+ # - exp Array
26
+ # `[:task, 'demo', :queue, :backlog, :sned, :pipe, :status_stream]`
27
+ # each key is followed by 0..n valid configurations information
28
+ # === @returns
29
+ # `Smash::Context`
30
+ def initialize(args)
31
+ byebug unless valid_args?(args)
32
+ unless valid_args?(args)
33
+ raise ArgumentError.new 'Can be either a Hash, JSON, or an Enumerable ' +
34
+ "arguments: #{args}"
35
+ end
36
+ @package = args
37
+ @structure = decipher(args)
38
+ end
39
+
40
+ # # Decipher figures out which translation method to use by making some simple
41
+ # # type checks, etc. and then routing the args to the correct method.
42
+ def decipher(args)
43
+ case args
44
+ when Hash
45
+ args
46
+ when String
47
+ translate_json(args)
48
+ when Enumerable
49
+ translate_list(args)
50
+ end
51
+ end
52
+
53
+ # Creates a flat Array of the 2-D Array that gets returned from `#simplify()`
54
+ # === @returns <Array>
55
+ def flatten
56
+ simplify.flatten
57
+ end
58
+
59
+ # Create an array version of @sructure by calling `#to_a()` on it
60
+ # === @returns Array<Array..n>
61
+ # TODO: Check if this has a limit to n-layers
62
+ def simplify
63
+ @structure.to_a
64
+ end
65
+
66
+ #
67
+ def structure
68
+ symbolize_keys(@structure)
69
+ end
70
+
71
+ # Valid scheme for @structure is assured by running the arguments through
72
+ # the decipher method, which is how @structure is set in `#new(args)`
73
+ def structure=(args)
74
+ @structure = decipher(args)
75
+ end
76
+
77
+ # Parse the given JSON
78
+ # === @param: json_string <String::Json>
79
+ # === @returns <Hash>
80
+ def translate_json(json_string)
81
+ begin
82
+ JSON.parse(json_string)
83
+ rescue JSON::ParserError
84
+ raise ArgumentError "Incorrectly formatted JSON"
85
+ end
86
+ end
87
+
88
+ # Re-layer this flattened Array or enumerable list so that it looks like the
89
+ # Hash that it used to be before the Smash::Context#flatten() method was called
90
+ # === @param list
91
+ # `<Array|List <Enumerable>`
92
+ # e.g.
93
+ # flat
94
+ # ```
95
+ # [
96
+ # object_name_1, config_1a, config_2a, ...,
97
+ # object_2, config_1b, etc,
98
+ # ...
99
+ # ]
100
+ # ```
101
+ # grouped
102
+ # or
103
+ # ```
104
+ # [
105
+ # [object_name_1, config_1a, config_2a, ...],
106
+ # [object_2, config_1b, etc],
107
+ # ...
108
+ # ]
109
+ # ```
110
+ # structured
111
+ # or
112
+ # ```
113
+ # [
114
+ # [object_name_1, [config_1a, config_2a, ...]],
115
+ # [object_2, [config_1b, etc]],
116
+ # ...
117
+ # ]
118
+ # ```
119
+ # returns
120
+ # ```
121
+ # {
122
+ # object_1: [config_1a, config_2a, ...],
123
+ # object_2: [config_1b, ...],
124
+ # ...
125
+ # }
126
+ # ```
127
+ # === @returns Hash
128
+ # If `#valid_package_hash?()` is called on this Hash, it will return true
129
+ def translate_list(list)
130
+ list.first.kind_of?(Enumerable) ? translate_simplified(list) : translate_flattened(list)
131
+ end
132
+
133
+ # Translates an Array into a valid @structure Hash
134
+ # === @param arr <Array>
135
+ # e.g.
136
+ # ```
137
+ # [:task, 'demo', :queue, 'name1', {other config hash},...,:pipe, 'name1']
138
+ # ```
139
+ # === @returns Hash
140
+ # calling `#valid_hash_format?()` on returned Hash will return true
141
+ def translate_flattened(list)
142
+ arr = list.to_a
143
+ results = []
144
+ # get the indexes of CloudPowers resources in arr
145
+ key_locations = arr.each_with_index.collect do |key, i, _|
146
+ i if available_resources.include?(to_pascal(key).to_sym)
147
+ end.reject(&:nil?)
148
+ key_locations << arr.size # add the last index into the ranges
149
+ # use each pair as ranges to slice up the Array
150
+ key_locations.each_cons(2) do |current_i, next_i|
151
+ results << [arr[current_i], arr[(current_i+1)..(next_i-1)]]
152
+ end
153
+
154
+ translate_simplified(results)
155
+ end
156
+
157
+ # Translates a 2D Array into a valid @structure Hash
158
+ # === @param arr <Array>
159
+ # e.g.
160
+ # ```
161
+ # [
162
+ # [:task, 'demo'],
163
+ # [:queue, 'name1', {other config hash},...],
164
+ # [:pipe, 'name1']
165
+ # ...
166
+ # ]
167
+ # ```
168
+ # - Needs to be a 2D Array
169
+ # - First object of each inner-Array is the key
170
+ # - All following values in that inner Array are separate configuration
171
+ # information pieces that can be used in the `#create!()` method for that particular resource
172
+ # === @returns Hash
173
+ # well formatted for the @structure
174
+ def translate_simplified(arr)
175
+ arr.inject({}) do |hash, key_config_map|
176
+ hash.tap do |h|
177
+ key = key_config_map.shift
178
+ h[key.to_sym] = *key_config_map.flatten
179
+ end
180
+ end
181
+ end
182
+
183
+ # Uses `#to_json()` on the @structure
184
+ # === @returns Hash
185
+ def to_json
186
+ structure.to_json
187
+ end
188
+
189
+ # The Context class can be initialized in any of the formats that a Context
190
+ # class _should_ exist in. It can be a vanilla version, where the @structure
191
+ # is a Hash, structured correctly or it can be serialized into JSON or it can
192
+ # be an Array
193
+ def valid_args?(args)
194
+ case args
195
+ when Hash
196
+ valid_hash_format?(args)
197
+ when String
198
+ valid_json_format?(args)
199
+ when Enumerable
200
+ valid_array_format?(args)
201
+ else
202
+ false
203
+ end
204
+ end
205
+
206
+ # Makes sure that the list is enumerable and that at least the first term
207
+ # is a resource name from Smash::CloudPowers. All other objects can
208
+ # potentially be configurations.
209
+ # @param list <Array|Enumerable>
210
+ # @returns Boolean
211
+ def valid_array_format?(list)
212
+ use = list.first.kind_of?(Enumerable) ? list.first.first : list.first
213
+ ((list.kind_of? Enumerable) && (available_resources.include?(to_pascal(use).to_sym)))
214
+ end
215
+
216
+ # Makes sure that each key is the name of something CloudPowers can interact with
217
+ # @param hash <Hash>
218
+ # @returns Boolean
219
+ def valid_hash_format?(hash)
220
+ keys_arr = hash.keys.map { |key| to_pascal(key).to_sym }
221
+ (keys_arr - available_resources).empty?
222
+ end
223
+
224
+ # Parses the json_string which yields a Hash or an exception. From there,
225
+ # use the #valid_hash_format?() method
226
+ def valid_json_format?(json_string)
227
+ begin
228
+ valid_hash_format?(JSON.parse(json_string))
229
+ rescue JSON::ParserError
230
+ false
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
@@ -1,8 +1,8 @@
1
1
  require 'logger'
2
2
  require 'pathname'
3
+ require 'uri'
3
4
  require 'syslog/logger'
4
5
  require_relative 'smash_error'
5
- require 'byebug'
6
6
 
7
7
  module Smash
8
8
  module CloudPowers
@@ -16,6 +16,13 @@ module Smash
16
16
  end
17
17
  end
18
18
 
19
+ # This is a way to find out if you are trying to work with a resource
20
+ # available to CloudPowers
21
+ # === @returns <Array>
22
+ def available_resources
23
+ [:Task].concat(Smash::CloudPowers.constants)
24
+ end
25
+
19
26
  def called_from
20
27
  File.expand_path(File.dirname($0))
21
28
  end
@@ -74,6 +81,10 @@ module Smash
74
81
  end
75
82
  end
76
83
 
84
+ def symbolize_keys(hash)
85
+ hash.inject({}) { |carry, (k, v)| carry.tap { |h| h[k.to_sym] = v } }
86
+ end
87
+
77
88
  # Gives the path from the project root to lib/tasks[/#{file}.rb]
78
89
  # @params:
79
90
  # * [file] <String>: name of a file
@@ -158,6 +169,10 @@ module Smash
158
169
  false
159
170
  end
160
171
  end
172
+
173
+ def valid_url?(url)
174
+ url =~ /\A#{URI::regexp}\z/
175
+ end
161
176
  end
162
177
  end
163
178
  end
@@ -8,6 +8,17 @@ module Smash
8
8
  include Smash::CloudPowers::Helper
9
9
  include Smash::CloudPowers::AwsResources
10
10
 
11
+ # A simpl Struct that acts as a Name to URL map
12
+ NUMap = Struct.new(:set_name, :set_url) do
13
+ def name
14
+ set_name || url.split('/').last # Queue names are last in the URL path
15
+ end
16
+
17
+ def url
18
+ set_url || Smash::CloudPowers::Queue::Board.new(name).best_guess_address
19
+ end
20
+ end
21
+
11
22
  # This method can be used to parse a queue name from its address. It can be handy if you need the name
12
23
  # of a queue but you don't want the overhead of creating a Board object.
13
24
  def board_name(url)
@@ -15,29 +26,30 @@ module Smash
15
26
  end
16
27
 
17
28
  # This method allows you to create a queue on SQS without explicitly creating a Board object
18
- # @params: name <String>: The name of the queue to be created
19
- # @returns: Queue::Board
29
+ # === @params: name <String>: The name of the queue to be created
30
+ # === @returns: Queue::Board
20
31
  def create_queue!(name)
21
32
  begin
22
- Board.create!(to_camel(name))
33
+ Smash::CloudPowers::Queue::Board.create!(to_camel(name))
23
34
  rescue Aws::SQS::Errors::QueueDeletedRecently => e
24
35
  sleep 5
25
36
  retry
26
37
  end
27
38
  end
28
39
 
29
- # This method builds a Queue::Board object for you to use but doesn't invoke the #create! method, so
30
- # no API call is made to create the queue on SQS. This can be used if the board already exists.
31
- # @params: name <String>: name of the queue you want to interact with
32
- # @returns: Queue::Board
40
+ # This method builds a Queue::Board object for you to use but doesn't
41
+ # invoke the #create! method, so no API call is made to create the queue
42
+ # on SQS. This can be used if the board already exists.
43
+ # === @params: name <String>: name of the queue you want to interact with
44
+ # === @returns: Queue::Board
33
45
  def build_queue(name)
34
- Board.build(to_camel(name))
46
+ Smash::CloudPowers::Queue::Board.build(to_camel(name))
35
47
  end
36
48
 
37
49
  # Deletes a queue message without caring about reading/interacting with the message.
38
50
  # This is usually used for progress tracking, ie; a Neuron takes a message from the Backlog, moves it to
39
51
  # WIP and deletes it from Backlog. Then repeats these steps for the remaining States in the Workflow
40
- # @params: queue <String>[, opts <Hash>]
52
+ # === @params: queue <String>[, opts <Hash>]
41
53
  # queue is the name of the queue to interact with
42
54
  # opts is a configuration hash for the SQS::QueuePoller
43
55
  def delete_queue_message(queue, opts = {})
@@ -48,8 +60,8 @@ module Smash
48
60
  end
49
61
 
50
62
  # This method is used to gain the approximate count of messages in a given queue
51
- # @params: board_url <String>: The URL for the board you need to get a count from
52
- # @returns: float representation of the count
63
+ # === @params: board_url <String>: The URL for the board you need to get a count from
64
+ # === @returns: float representation of the count
53
65
  def get_queue_message_count(board_url)
54
66
  sqs.get_queue_attributes(
55
67
  queue_url: board_url,
@@ -57,8 +69,8 @@ module Smash
57
69
  ).attributes['ApproximateNumberOfMessages'].to_f
58
70
  end
59
71
 
60
- # @params: board<String|symbol>: The name of the board
61
- # @returns a message and deletes it from its origin
72
+ # === @params: board<String|symbol>: The name of the board
73
+ # === @returns a message and deletes it from its origin
62
74
  def pluck_queue_message(board)
63
75
  poll(board) do |msg, poller|
64
76
  poller.delete_message(msg)
@@ -68,11 +80,11 @@ module Smash
68
80
 
69
81
  # Polls the given board with the given options hash and a block that interacts with
70
82
  # the message that is retrieved from the queue
71
- # @params: board <String>[, opts <Hash>]
83
+ # === @params: board <String>[, opts <Hash>]
72
84
  # board is the name of the queue you want to poll
73
85
  # opts can have any AWS::SQS polling option
74
86
  # &block is the block that is used to interact with the message that was retrieved
75
- # @returns the results from the `message` and the `block` that interacts with the message(s)
87
+ # === @returns the results from the `message` and the `block` that interacts with the message(s)
76
88
  def poll(board, opts = {})
77
89
  this_poller = queue_poller(board)
78
90
  results = nil
@@ -87,10 +99,10 @@ module Smash
87
99
  # This method can be used to gain a SQS::QueuePoller. It creates a Board object,
88
100
  # the Board then sends the API call to SQS to create the queue and sets an instance
89
101
  # variable, using the board's name, to the Board object itself
90
- # @params: board_name <String>: name of the Queue you want to gain a QueuePoller for
91
- # @returns: @<board_name:Queue::Board>
102
+ # === @params: board_name <String>: name of the Queue you want to gain a QueuePoller for
103
+ # === @returns: @<board_name:Queue::Board>
92
104
  def queue_poller(board_name)
93
- board = Board.create!(board_name)
105
+ board = Smash::CloudPowers::Queue::Board.create!(board_name)
94
106
 
95
107
  unless instance_variable_defined?(board.i_var)
96
108
  instance_variable_set(
@@ -107,15 +119,15 @@ module Smash
107
119
  end
108
120
 
109
121
  # Searches for a queue based on the name
110
- # @params: name <String>
111
- # @returns: queue_urls <Array>
122
+ # === @params: name <String>
123
+ # === @returns: queue_urls <Array>
112
124
  def queue_search(name)
113
125
  sqs.list_queues(queue_name_prefix: name).queue_urls
114
126
  end
115
127
 
116
128
  # Sends a given message to a given queue
117
- # @params: address <String>: address of the queue you want to interact with
118
- # @returns: queue_urls <Array<String>>
129
+ # === @params: address <String>: address of the queue you want to interact with
130
+ # === @returns: queue_urls <Array<String>> # TODO: verify this. maybe it was late...
119
131
  def send_queue_message(address, message)
120
132
  sqs.send_message(
121
133
  queue_url: address,
@@ -1,3 +1,3 @@
1
1
  module CloudPowers
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -1,32 +1,34 @@
1
1
  module Smash
2
- class Workflow
3
- attr_accessor :all_states, :first, :last, :previous, :states
2
+ module CloudPowers
3
+ class Workflow
4
+ attr_accessor :all_states, :first, :last, :previous, :states
4
5
 
5
- def initialize(statez = nil)
6
- # TODO: figure out wtf here with the i-vars changing each other
7
- @all_states = statez || [:backlog, :wip, :done]
8
- @states = statez || [:backlog, :wip, :done]
9
- @first = (statez || [:backlog, :wip, :done]).first
10
- @last = (statez || [:backlog, :wip, :done]).last
11
- @previous = [(statez || [:backlog, :wip, :done]).first]
12
- end
6
+ def initialize(statez = nil)
7
+ # TODO: figure out wtf here with the i-vars changing each other
8
+ @all_states = statez || [:backlog, :wip, :done]
9
+ @states = statez || [:backlog, :wip, :done]
10
+ @first = (statez || [:backlog, :wip, :done]).first
11
+ @last = (statez || [:backlog, :wip, :done]).last
12
+ @previous = [(statez || [:backlog, :wip, :done]).first]
13
+ end
13
14
 
14
- def current
15
- @states.first
16
- end
15
+ def current
16
+ @states.first
17
+ end
17
18
 
18
- def finished?
19
- @states.first == @states.last
20
- end
19
+ def finished?
20
+ @states.first == @states.last
21
+ end
21
22
 
22
- def next
23
- finished ? @states.first : @states[1]
24
- end
23
+ def next
24
+ finished ? @states.first : @states[1]
25
+ end
25
26
 
26
- def next!
27
- return if finished?
28
- @previous << @states.shift
29
- @states.first
27
+ def next!
28
+ return if finished?
29
+ @previous << @states.shift
30
+ @states.first
31
+ end
30
32
  end
31
33
  end
32
34
  end
data/lib/cloud_powers.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'cloud_powers/auth'
2
2
  require 'cloud_powers/aws_resources'
3
+ require 'cloud_powers/context'
3
4
  require 'cloud_powers/delegator'
4
5
  require 'cloud_powers/helper'
5
6
  require 'cloud_powers/self_awareness'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloud_powers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Phillipps
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-30 00:00:00.000000000 Z
11
+ date: 2016-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport-core-ext
@@ -151,13 +151,14 @@ dependencies:
151
151
  - !ruby/object:Gem::Version
152
152
  version: '3.0'
153
153
  description: |2
154
- CloudPowers is a wrapper around AWS and other cloud services.
155
- It was developed specifically for the Brain project
156
- but hopefully can be used in any other ruby project that needs to use cloud
157
- service providers' resources. Version 0.2.3 has a little EC2, S3, SQS, SNS
158
- and Kinesis. V-0.2.4 should be out in a day or so and it will include IoT
159
- basics and specs too. This project is actively being developed, so more
160
- additions, specs and docs will be updated frequently. I always welcome input.
154
+ CloudPowers is a wrapper around AWS and in the future, other cloud service Providers.
155
+ It was developed specifically for the Brain project but hopefully can be used
156
+ in any other ruby project that needs to use cloud service providers' resources.
157
+ Version 0.2.3 has a little EC2, S3, SQS, SNS and Kinesis. V 0.2.4 includes
158
+ a Context a Workflow revamp. Websockets will be next.
159
+ This project is actively being developed, so more additions, specs and docs
160
+ will be updated frequently with new funcionality but the gem will follow good
161
+ practices for versioning. I always welcome input.
161
162
  Enjoy!
162
163
  email: adam.phillipps@gmail.com
163
164
  executables: []
@@ -181,6 +182,7 @@ files:
181
182
  - lib/cloud_powers/.DS_Store
182
183
  - lib/cloud_powers/auth.rb
183
184
  - lib/cloud_powers/aws_resources.rb
185
+ - lib/cloud_powers/context.rb
184
186
  - lib/cloud_powers/delegator.rb
185
187
  - lib/cloud_powers/helper.rb
186
188
  - lib/cloud_powers/self_awareness.rb