glassfrog 0.3.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.
@@ -0,0 +1,40 @@
1
+ require 'glassfrog/rest/request'
2
+
3
+ module Glassfrog
4
+ module REST
5
+ #
6
+ # Encapsulates all GET requests.
7
+ #
8
+ module Get
9
+ #
10
+ # Sends a GET request.
11
+ # @param client [Glassfrog::Client] Client that will send the request.
12
+ # @param path [String] Path to send request to.
13
+ # @param options [Hash] Options being sent in the request.
14
+ #
15
+ # @return [Array<Hash>] Array containing Hashes of objects fetched.
16
+ def self.get(client, path, options)
17
+ path = options[:id] ? path + '/' + options.delete(:id).to_s : path
18
+ Glassfrog::REST::Request.new(client, :get, path, options).perform
19
+ end
20
+
21
+ #
22
+ # Handles GET requests for objects that do not have a native get with ID method on GlassFrog.
23
+ # @param client [Glassfrog::Client] Client that will send the request.
24
+ # @param type [Symbol] The symbol of the object type being fetched to be used as a key in the response hash.
25
+ # @param path [String] Path to send request to.
26
+ # @param options [Hash] Options being sent in the request.
27
+ #
28
+ # @return [Array<Hash>] Array containing Hashes of objects fetched.
29
+ def self.irregular_get(client, type, path, options)
30
+ if options.is_a?(Hash) && options[:id]
31
+ response = get(client, path, {})
32
+ if response[type] then response[type].select! { |object| object[:id] == options[:id] } end
33
+ else
34
+ response = get(client, path, options)
35
+ end
36
+ response
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ require 'glassfrog/rest/request'
2
+
3
+ module Glassfrog
4
+ module REST
5
+ #
6
+ # Encapsulates all PATCH requests.
7
+ #
8
+ module Patch
9
+ #
10
+ # Sends a PATCH request.
11
+ # @param client [Glassfrog::Client] Client that will send the request.
12
+ # @param path [String] Path to send request to.
13
+ # @param options [Hash] Options being sent in the request.
14
+ #
15
+ # @return [Boolean] Whether request was successful.
16
+ def self.patch(client, path, options)
17
+ Glassfrog::REST::Request.new(client, :patch, path, options).perform
18
+ end
19
+
20
+ #
21
+ # Turns options into PATCH form.
22
+ # @param options [Hash] Options to be formified.
23
+ # @param type [Class] Class being targeted by the request.
24
+ #
25
+ # @return [Hash] Formified options.
26
+ def self.formify(options, type)
27
+ options.keys.map do |key|
28
+ { op: 'replace',
29
+ path: type::PATH + '/0/' + key.to_s,
30
+ value: options[key] }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ require 'glassfrog/rest/request'
2
+
3
+ module Glassfrog
4
+ module REST
5
+ #
6
+ # Encapsulates all POST requests.
7
+ #
8
+ module Post
9
+ #
10
+ # Sends a POST request.
11
+ # @param client [Glassfrog::Client] Client that will send the request.
12
+ # @param path [String] Path to send request to.
13
+ # @param options [Hash] Options being sent in the request.
14
+ #
15
+ # @return [Array<Hash>] Array containing a Hash for the object created.
16
+ def self.post(client, path, options)
17
+ Glassfrog::REST::Request.new(client, :post, path, options).perform
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,88 @@
1
+ require 'addressable/uri'
2
+ require 'glassfrog/client'
3
+ require 'glassfrog/error'
4
+ require 'glassfrog/utils/utils'
5
+
6
+ module Glassfrog
7
+ module REST
8
+ #
9
+ # Encapsulates an HTTP Request.
10
+ #
11
+ class Request
12
+ include Glassfrog::Utils
13
+ # @return [Glassfrog::Client]
14
+ attr_accessor :client
15
+ # @return [Hash]
16
+ attr_accessor :headers, :options
17
+ # @return [Symbol]
18
+ attr_accessor :request_method
19
+ # @return [Addressable::URI]
20
+ attr_accessor :uri
21
+ ROOT_URL = 'https://glassfrog.holacracy.org/api/v3'
22
+
23
+ REQUEST_ASSOCIATIONS = {
24
+ get: :params,
25
+ post: :json,
26
+ patch: :json,
27
+ delete: :form
28
+ }
29
+
30
+ #
31
+ # Initializes a new Request object.
32
+ # @param client [Glassfrog::Client] The client that will send the request.
33
+ # @param request_method [Symbol] The type of request that will be sent.
34
+ # @param path [String] The path (minus the root) that the request will be sent to.
35
+ # @param options [Hash] The options that will be included in the request.
36
+ #
37
+ # @return [Glassfrog::Request] The initialized Request object.
38
+ def initialize(client, request_method, path, options)
39
+ @client = client
40
+ @headers = client.headers
41
+ @request_method = request_method
42
+ @uri = Addressable::URI.parse(path.start_with?('http') ? path : ROOT_URL + path)
43
+ @options = options
44
+ end
45
+
46
+ #
47
+ # Sends the Request.
48
+ #
49
+ # @return [Array<Hash>, Boolean] The fetched or created parameters, or boolean reflecting whether the request was successful.
50
+ def perform
51
+ options_key = REQUEST_ASSOCIATIONS[@request_method]
52
+ response = @client.http.headers(@headers).public_send(@request_method, @uri.to_s, options_key => @options)
53
+ fail_or_return_response_body(response.code, response, response.headers)
54
+ end
55
+
56
+ private
57
+
58
+ #
59
+ # Returns an error if there was one, or parses the fetched object.
60
+ # @param code [Integer] The HTTP response code.
61
+ # @param body [String] The body of the HTTP response.
62
+ # @param headers [Hash] The HTTP response headers.
63
+ #
64
+ # @return [Array<Hash>, Boolean] The fetched or created parameters or boolean reflecting whether the request was successful.
65
+ def fail_or_return_response_body(code, body, headers)
66
+ error = error(code, body, headers)
67
+ fail(error) if error
68
+ if @request_method == :patch || @request_method == :delete
69
+ true
70
+ else
71
+ symbolize_keys(body.parse)
72
+ end
73
+ end
74
+
75
+ #
76
+ # Generates an error if the code corresponds to one.
77
+ # @param code [Integer] The HTTP response code.
78
+ # @param body [String] The body of the HTTP response.
79
+ # @param headers [Hash] The HTTP response headers.
80
+ #
81
+ # @return [Glassfrog::Error, nil] The corresponding error, or nil.
82
+ def error(code, body, headers)
83
+ klass = Glassfrog::Error::ERRORS[code]
84
+ klass.from_response(code, body, headers) if !klass.nil?
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,7 @@
1
+ module Glassfrog
2
+ #
3
+ # Encapsulates all HTTP functionality.
4
+ #
5
+ module REST
6
+ end
7
+ end
@@ -0,0 +1,94 @@
1
+ require 'glassfrog/base'
2
+ require 'glassfrog/rest/get'
3
+ require 'glassfrog/rest/patch'
4
+
5
+ module Glassfrog
6
+ #
7
+ # Encapsulates GlassFrog Roles.
8
+ #
9
+ class Role < Glassfrog::Base
10
+ # @return [String]
11
+ attr_accessor :name, :purpose
12
+ # @return [Hash]
13
+ attr_accessor :links
14
+ PATH = '/roles'
15
+ PATCH_PATH = '/roles/0/links/people/'
16
+ TYPE = :roles
17
+
18
+ #
19
+ # Sends a GET request for Role(s) to GlassFrog.
20
+ # @param client [Glassfrog::Client] The client that will send the request. Contains the API key.
21
+ # @param options [Hash, Glassfrog::Base] The options used to find the correct Roles(s).
22
+ #
23
+ # @return [Array<Glassfrog::Role>] The array of Role(s) fetched from GlassFrog.
24
+ def self.get(client, options)
25
+ response = Glassfrog::REST::Get.get(client, PATH, options)
26
+ response[TYPE].map { |object| self.new(object) }
27
+ end
28
+
29
+ #
30
+ # Sends a PATCH request to update a ChecklistItem on GlassFrog. Only updates the People assigned to the Role.
31
+ # @param client [Glassforg::Client] The client that will send the request. Contains the API key.
32
+ # @param identifier [Integer] The ID of the ChecklistItem to be updated.
33
+ # @param options [Hash, Glassfrog::Base] The options used to update the ChecklistItem.
34
+ #
35
+ # @return [Boolean] Whether the request failed or not.
36
+ def self.patch(client, identifier, options)
37
+ path = PATH + '/' + identifier.to_s
38
+ current_object = self.get(client, { id: identifier }).first
39
+ options = options.is_a?(Glassfrog::Role) ? parse_options(options.hashify) : parse_options(options)
40
+ if current_object.links && current_object.links[:people] && options[:people]
41
+ (options[:people] - current_object.links[:people]).each do |person_id|
42
+ o = formify_role_patch({ person_id: person_id }, 'add')
43
+ if !Glassfrog::REST::Patch.patch(client, path, o) then return false end
44
+ end
45
+ (current_object.links[:people] - options[:people]).each do |person_id|
46
+ o = formify_role_patch({ person_id: person_id }, 'remove')
47
+ if !Glassfrog::REST::Patch.patch(client, path, o) then return false end
48
+ end
49
+ else
50
+ raise(ArgumentError, "No people found")
51
+ end
52
+ true
53
+ end
54
+
55
+ #
56
+ # Returns the name as a symbol with underscores instead of spaces.
57
+ #
58
+ # @return [Symbol] The name as a symbol.
59
+ def name_parameterized
60
+ parameterize(@name)
61
+ end
62
+
63
+ private
64
+
65
+ PARAMS = [
66
+ :people
67
+ ]
68
+
69
+ #
70
+ # Grabs only the parameters accepted by GlassFrog.
71
+ # @param options [Hash] Inputed options.
72
+ #
73
+ # @return [Hash] Valid GlassFrog options.
74
+ def self.parse_options(options)
75
+ options[:people] = options[:links][:people] if options[:links] && options[:links][:people]
76
+ params_hash = Hash.new
77
+ PARAMS.each { |param| params_hash[param] = options[param] if options[param] }
78
+ params_hash
79
+ end
80
+
81
+ #
82
+ # Turns options into the format to update GlassFrog.
83
+ # @param options [Hash] Hash containing the Person to update the Roles of.
84
+ # @param operation [String] The operation to perform, either 'Remove' or 'Add'.
85
+ #
86
+ # @return [Hash] Formified options.
87
+ def self.formify_role_patch(options, operation)
88
+ options.keys.map do |key|
89
+ { op: operation,
90
+ path: PATCH_PATH + options[key].to_s }
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,29 @@
1
+ require 'glassfrog/base'
2
+ require 'glassfrog/rest/get'
3
+
4
+ module Glassfrog
5
+ #
6
+ # Encapsulates GlassFrog Actions.
7
+ #
8
+ class Trigger < Glassfrog::Base
9
+ # @return [String]
10
+ attr_accessor :description, :created_at
11
+ # @return [Boolean]
12
+ attr_accessor :private_to_circle
13
+ # @return [Hash]
14
+ attr_accessor :links
15
+ PATH = '/triggers'
16
+ TYPE = :triggers
17
+
18
+ #
19
+ # Sends a GET request for Trigger(s) to GlassFrog.
20
+ # @param client [Glassfrog::Client] The client that will send the request. Contains the API key.
21
+ # @param options [Hash, Glassfrog::Base] The options used to find the correct Triggers(s).
22
+ #
23
+ # @return [Array<Glassfrog::Trigger>] The array of Trigger(s) fetched from GlassFrog.
24
+ def self.get(client, options)
25
+ response = Glassfrog::REST::Get.irregular_get(client, TYPE, PATH, options)
26
+ response[TYPE] ? response[TYPE].map { |object| self.new(object) } : []
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ module Glassfrog
2
+ #
3
+ # Encapsulates utilities for building a circle hierarchy based on supporting roles/supported circles.
4
+ #
5
+ module Graph
6
+ #
7
+ # Finds the root of the 'circle tree' by finding the role that does not belong to a circle.
8
+ # @param circles [Array<Glassfrog::Circle>] Array of circle objects (e.g. returned from GET or post-hierarchy build).
9
+ # @param roles [Array<Glassfrog::Role>] Array of role objects (e.g. returned from GET).
10
+ #
11
+ # @return [Glassfrog::Circle] The root circle.
12
+ def self.root(circles, roles)
13
+ root_role = roles.select { |role| role.links[:circle].nil? }.first
14
+ circles.select { |circle| circle.id == root_role.links[:supporting_circle] }.first
15
+ end
16
+
17
+ #
18
+ # Populates each circle's sub_circles array with its respective sub circles.
19
+ # This done by finding all of the supporting roles and the circle to which they belong.
20
+ # @param client [Glassfrog::Client] Client used to make the get requests (unless circles and roles are provided).
21
+ # @param circles=nil [Array<Glassfrog::Circle>] Array of circle objects (used instead of a get request).
22
+ # @param roles=nil [Array<Glassfrog::Role>] Array of role objects (used instead of a get request).
23
+ #
24
+ # @return [Glassfrog::Circle] The root circle of the 'circle tree'.
25
+ def self.hierarchy(client, circles=nil, roles=nil)
26
+ circles ||= client.get(:circles)
27
+ roles ||= client.get(:roles)
28
+ circle_hash, role_hash = Hash[circles.map { |circle| [circle.id, circle] }], Hash[client.get(:roles).map { |role| [role.id, role] }]
29
+ sub_circles = circles.select { |circle| circle.links[:supported_role].is_a? Fixnum }
30
+ sub_circles_hash = Hash[sub_circles.map { |circle| [circle, circle_hash[role_hash[circle.links[:supported_role]].links[:circle]]] }]
31
+ sub_circles_hash.each do |sub_circle, parent_circle|
32
+ (parent_circle.sub_circles ? parent_circle.sub_circles.push(sub_circle) : parent_circle.sub_circles = [sub_circle]) if parent_circle
33
+ end
34
+ root(circles, roles)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,56 @@
1
+ module Glassfrog
2
+ #
3
+ # Utilites for handling requests.
4
+ #
5
+ module Utils
6
+ #
7
+ # Turns a string into a lowercase, underscored symbol for use as a parameter.
8
+ # @param value [String] The string.
9
+ #
10
+ # @return [Symbol] The symbol.
11
+ def parameterize(value)
12
+ value.to_s.downcase.tr(" ", "_").to_sym
13
+ end
14
+
15
+ #
16
+ # Turns all the values into symbols in an array or all the keys in a hash.
17
+ # @param object [Hash, Array, Object] Object to be symbolized.
18
+ #
19
+ # @return [Hash, Array, Object] Symbolized object.
20
+ def symbolize_keys(object)
21
+ if object.is_a?(Array)
22
+ object.each_with_index do |val, index|
23
+ object[index] = symbolize_keys(val)
24
+ end
25
+ elsif object.is_a?(Hash)
26
+ object.keys.each do |key|
27
+ object[key.to_sym] = symbolize_keys(object.delete(key))
28
+ end
29
+ end
30
+ object
31
+ end
32
+
33
+ #
34
+ # Grabs ID out of an object.
35
+ # @param object [Integer, String, URI, Glassfrog::Base] The object to fetch ID from.
36
+ # @param klass [Class] The expected class.
37
+ #
38
+ # @return [Integer, nil] ID or nil.
39
+ def extract_id(object, klass)
40
+ case object
41
+ when ::Integer
42
+ object
43
+ when ::String
44
+ object.split('/').last.to_i
45
+ when ::Hash
46
+ object[:id] || object[:ID]
47
+ when URI, Addressable::URI
48
+ object.path.split('/').last.to_i
49
+ when klass
50
+ object.id
51
+ else
52
+ nil
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module Glassfrog
2
+ VERSION = "0.3.0"
3
+ end
data/lib/glassfrog.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'glassfrog/version'
2
+ require 'glassfrog/client'
3
+
4
+ #
5
+ # Encapsulates all of GlassFrog functionality.
6
+ #
7
+ module Glassfrog
8
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: glassfrog
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Undercurrent
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-07-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: http
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rack-cache
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
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: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.10'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ - robert.wells@undercurrent.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .rspec
106
+ - .travis.yml
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - bin/console
112
+ - bin/setup
113
+ - glassfrog.gemspec
114
+ - lib/glassfrog.rb
115
+ - lib/glassfrog/action.rb
116
+ - lib/glassfrog/base.rb
117
+ - lib/glassfrog/checklist_item.rb
118
+ - lib/glassfrog/circle.rb
119
+ - lib/glassfrog/client.rb
120
+ - lib/glassfrog/error.rb
121
+ - lib/glassfrog/metric.rb
122
+ - lib/glassfrog/person.rb
123
+ - lib/glassfrog/project.rb
124
+ - lib/glassfrog/rest/delete.rb
125
+ - lib/glassfrog/rest/get.rb
126
+ - lib/glassfrog/rest/patch.rb
127
+ - lib/glassfrog/rest/post.rb
128
+ - lib/glassfrog/rest/request.rb
129
+ - lib/glassfrog/rest/rest.rb
130
+ - lib/glassfrog/role.rb
131
+ - lib/glassfrog/trigger.rb
132
+ - lib/glassfrog/utils/graph.rb
133
+ - lib/glassfrog/utils/utils.rb
134
+ - lib/glassfrog/version.rb
135
+ homepage: https://github.com/UCSoftware/glassfrog-ruby
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 2.0.14
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: A Ruby interface for the GlassFrog API.
159
+ test_files: []