glassfrog 0.3.0

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