rainforest 1.0.7 → 2.0.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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -6
  3. data/.travis.yml +13 -1
  4. data/Gemfile +6 -0
  5. data/README.md +72 -21
  6. data/Rakefile +6 -13
  7. data/VERSION +1 -1
  8. data/bin/rainforest-console +1 -1
  9. data/gemfiles/default-with-activesupport.gemfile +8 -1
  10. data/gemfiles/json.gemfile +10 -2
  11. data/gemfiles/yajl.gemfile +10 -2
  12. data/lib/rainforest.rb +61 -240
  13. data/lib/rainforest/apibits/api_client.rb +28 -0
  14. data/lib/rainforest/apibits/api_endpoint.rb +11 -0
  15. data/lib/rainforest/apibits/api_list.rb +88 -0
  16. data/lib/rainforest/apibits/api_method.rb +95 -0
  17. data/lib/rainforest/apibits/api_object.rb +52 -0
  18. data/lib/rainforest/apibits/api_resource.rb +153 -0
  19. data/lib/rainforest/apibits/headers_builder.rb +47 -0
  20. data/lib/rainforest/apibits/params_builder.rb +27 -0
  21. data/lib/rainforest/apibits/path_builder.rb +38 -0
  22. data/lib/rainforest/apibits/requester.rb +104 -0
  23. data/lib/rainforest/apibits/util.rb +51 -0
  24. data/lib/rainforest/clients/default_client.rb +59 -0
  25. data/lib/rainforest/endpoints/client_stats_endpoint.rb +11 -0
  26. data/lib/rainforest/endpoints/environment_runs_endpoint.rb +11 -0
  27. data/lib/rainforest/endpoints/environments_endpoint.rb +48 -0
  28. data/lib/rainforest/endpoints/generator_rows_endpoint.rb +17 -0
  29. data/lib/rainforest/endpoints/generators_endpoint.rb +48 -0
  30. data/lib/rainforest/endpoints/integrations_endpoint.rb +33 -0
  31. data/lib/rainforest/endpoints/run_tests_endpoint.rb +20 -0
  32. data/lib/rainforest/endpoints/runs_endpoint.rb +48 -0
  33. data/lib/rainforest/endpoints/schedules_endpoint.rb +48 -0
  34. data/lib/rainforest/endpoints/site_environments_endpoint.rb +24 -0
  35. data/lib/rainforest/endpoints/sites_endpoint.rb +39 -0
  36. data/lib/rainforest/endpoints/tests_endpoint.rb +57 -0
  37. data/lib/rainforest/endpoints/users_endpoint.rb +48 -0
  38. data/lib/rainforest/errors/api_connection_error.rb +1 -1
  39. data/lib/rainforest/errors/api_error.rb +32 -1
  40. data/lib/rainforest/errors/authentication_error.rb +1 -1
  41. data/lib/rainforest/errors/rainforest_error.rb +2 -9
  42. data/lib/rainforest/resources/client_stats.rb +31 -0
  43. data/lib/rainforest/resources/environment.rb +73 -0
  44. data/lib/rainforest/resources/generator.rb +73 -0
  45. data/lib/rainforest/resources/integration.rb +52 -0
  46. data/lib/rainforest/resources/run.rb +93 -0
  47. data/lib/rainforest/resources/schedule.rb +63 -0
  48. data/lib/rainforest/resources/site.rb +53 -0
  49. data/lib/rainforest/resources/site_environment.rb +40 -0
  50. data/lib/rainforest/resources/test.rb +98 -0
  51. data/lib/rainforest/resources/user.rb +75 -0
  52. data/rainforest.gemspec +8 -6
  53. data/test/rainforest/api_client_test.rb +51 -0
  54. data/test/rainforest/api_endpoint_test.rb +13 -0
  55. data/test/rainforest/api_list_test.rb +49 -0
  56. data/test/rainforest/api_method_test.rb +78 -0
  57. data/test/rainforest/headers_builder_test.rb +28 -0
  58. data/test/rainforest/params_builder_test.rb +57 -0
  59. data/test/rainforest/path_builder_test.rb +50 -0
  60. data/test/rainforest/requester_test.rb +86 -0
  61. data/test/rainforest/util_test.rb +41 -19
  62. data/test/test_data.rb +72 -0
  63. data/test/test_helper.rb +17 -134
  64. metadata +99 -59
  65. data/CONTRIBUTORS +0 -2
  66. data/History.txt +0 -4
  67. data/LICENSE +0 -21
  68. data/lib/data/ca-certificates.crt +0 -3918
  69. data/lib/rainforest/api_operations/create.rb +0 -16
  70. data/lib/rainforest/api_operations/delete.rb +0 -11
  71. data/lib/rainforest/api_operations/list.rb +0 -16
  72. data/lib/rainforest/api_operations/update.rb +0 -61
  73. data/lib/rainforest/api_resource.rb +0 -33
  74. data/lib/rainforest/errors/invalid_request_error.rb +0 -10
  75. data/lib/rainforest/json.rb +0 -21
  76. data/lib/rainforest/list_object.rb +0 -35
  77. data/lib/rainforest/rainforest_object.rb +0 -165
  78. data/lib/rainforest/run.rb +0 -8
  79. data/lib/rainforest/singleton_api_resource.rb +0 -20
  80. data/lib/rainforest/util.rb +0 -100
  81. data/test/rainforest/api_resource_test.rb +0 -11
  82. data/test/rainforest/auth_test.rb +0 -24
  83. data/test/rainforest/list_object_test.rb +0 -7
  84. data/test/rainforest/metadata_test.rb +0 -7
  85. data/test/rainforest/run_test.rb +0 -36
@@ -1,16 +0,0 @@
1
- module Rainforest
2
- module APIOperations
3
- module Create
4
- module ClassMethods
5
- def create(params={}, api_key=nil)
6
- response, api_key = Rainforest.request(:post, self.url, api_key, params)
7
- Util.convert_to_rainforest_object(response, api_key)
8
- end
9
- end
10
-
11
- def self.included(base)
12
- base.extend(ClassMethods)
13
- end
14
- end
15
- end
16
- end
@@ -1,11 +0,0 @@
1
- module Rainforest
2
- module APIOperations
3
- module Delete
4
- def delete
5
- response, api_key = Rainforest.request(:delete, url, @api_key)
6
- refresh_from(response, api_key)
7
- self
8
- end
9
- end
10
- end
11
- end
@@ -1,16 +0,0 @@
1
- module Rainforest
2
- module APIOperations
3
- module List
4
- module ClassMethods
5
- def all(filters={}, api_key=nil)
6
- response, api_key = Rainforest.request(:get, url, api_key, filters)
7
- Util.convert_to_rainforest_object(response, api_key)
8
- end
9
- end
10
-
11
- def self.included(base)
12
- base.extend(ClassMethods)
13
- end
14
- end
15
- end
16
- end
@@ -1,61 +0,0 @@
1
- module Rainforest
2
- module APIOperations
3
- module Update
4
- def save
5
- values = serialize_params(self)
6
-
7
- if @values[:metadata]
8
- values[:metadata] = serialize_metadata
9
- end
10
-
11
- if values.length > 0
12
- values.delete(:id)
13
-
14
- if self.id
15
- response, api_key = Rainforest.request(:put, url + "/#{self.id}", @api_key, values)
16
- else
17
- response, api_key = Rainforest.request(:post, url, @api_key, values)
18
- end
19
- refresh_from(response, api_key)
20
- end
21
- self
22
- end
23
-
24
- def serialize_metadata
25
- if @unsaved_values.include?(:metadata)
26
- # the metadata object has been reassigned
27
- # i.e. as object.metadata = {key => val}
28
- metadata_update = @values[:metadata] # new hash
29
- new_keys = metadata_update.keys.map(&:to_sym)
30
- # remove keys at the server, but not known locally
31
- keys_to_unset = @previous_metadata.keys - new_keys
32
- keys_to_unset.each {|key| metadata_update[key] = ''}
33
-
34
- metadata_update
35
- else
36
- # metadata is a RainforestObject, and can be serialized normally
37
- serialize_params(@values[:metadata])
38
- end
39
- end
40
-
41
- def serialize_params(obj)
42
- case obj
43
- when nil
44
- ''
45
- when RainforestObject
46
- unsaved_keys = obj.instance_variable_get(:@unsaved_values)
47
- obj_values = obj.instance_variable_get(:@values)
48
- update_hash = {}
49
-
50
- unsaved_keys.each do |k|
51
- update_hash[k] = serialize_params(obj_values[k])
52
- end
53
-
54
- update_hash
55
- else
56
- obj
57
- end
58
- end
59
- end
60
- end
61
- end
@@ -1,33 +0,0 @@
1
- module Rainforest
2
- class APIResource < RainforestObject
3
- def self.class_name
4
- self.name.split('::')[-1]
5
- end
6
-
7
- def self.url()
8
- if self == APIResource
9
- raise NotImplementedError.new('APIResource is an abstract class. You should perform actions on its subclasses (Charge, Customer, etc.)')
10
- end
11
- "/#{CGI.escape(class_name.downcase)}s"
12
- end
13
-
14
- def url
15
- unless id = self['id']
16
- raise InvalidRequestError.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}", 'id')
17
- end
18
- "#{self.class.url}/#{CGI.escape(id.to_s)}"
19
- end
20
-
21
- def refresh
22
- response, api_key = Rainforest.request(:get, url, @api_key, @retrieve_options)
23
- refresh_from(response, api_key)
24
- self
25
- end
26
-
27
- def self.retrieve(id, api_key=nil)
28
- instance = self.new(id, api_key)
29
- instance.refresh
30
- instance
31
- end
32
- end
33
- end
@@ -1,10 +0,0 @@
1
- module Rainforest
2
- class InvalidRequestError < RainforestError
3
- attr_accessor :param
4
-
5
- def initialize(message, param, http_status=nil, http_body=nil, json_body=nil)
6
- super(message, http_status, http_body, json_body)
7
- @param = param
8
- end
9
- end
10
- end
@@ -1,21 +0,0 @@
1
- module Rainforest
2
- module JSON
3
- if MultiJson.respond_to?(:dump)
4
- def self.dump(*args)
5
- MultiJson.dump(*args)
6
- end
7
-
8
- def self.load(*args)
9
- MultiJson.load(*args)
10
- end
11
- else
12
- def self.dump(*args)
13
- MultiJson.encode(*args)
14
- end
15
-
16
- def self.load(*args)
17
- MultiJson.decode(*args)
18
- end
19
- end
20
- end
21
- end
@@ -1,35 +0,0 @@
1
- module Rainforest
2
- class ListObject < RainforestObject
3
-
4
- def [](k)
5
- case k
6
- when String, Symbol
7
- super
8
- else
9
- raise ArgumentError.new("You tried to access the #{k.inspect} index, but ListObject types only support String keys. (HINT: List calls return an object with a 'data' (which is the data array). You likely want to call #data[#{k.inspect}])")
10
- end
11
- end
12
-
13
- def each(&blk)
14
- self.data.each(&blk)
15
- end
16
-
17
- def retrieve(id, api_key=nil)
18
- api_key ||= @api_key
19
- response, api_key = Rainforest.request(:get,"#{url}/#{CGI.escape(id)}", api_key)
20
- Util.convert_to_rainforest_object(response, api_key)
21
- end
22
-
23
- def create(params={}, api_key=nil)
24
- api_key ||= @api_key
25
- response, api_key = Rainforest.request(:post, url, api_key, params)
26
- Util.convert_to_rainforest_object(response, api_key)
27
- end
28
-
29
- def all(params={}, api_key=nil)
30
- api_key ||= @api_key
31
- response, api_key = Rainforest.request(:get, url, api_key, params)
32
- Util.convert_to_rainforest_object(response, api_key)
33
- end
34
- end
35
- end
@@ -1,165 +0,0 @@
1
- module Rainforest
2
- class RainforestObject
3
- include Enumerable
4
-
5
- attr_accessor :api_key
6
- @@permanent_attributes = Set.new([:api_key, :id])
7
-
8
- # The default :id method is deprecated and isn't useful to us
9
- if method_defined?(:id)
10
- undef :id
11
- end
12
-
13
- def initialize(id=nil, api_key=nil)
14
- # parameter overloading!
15
- if id.kind_of?(Hash)
16
- @retrieve_options = id.dup
17
- @retrieve_options.delete(:id)
18
- id = id[:id]
19
- else
20
- @retrieve_options = {}
21
- end
22
-
23
- @api_key = api_key
24
- @values = {}
25
- # This really belongs in APIResource, but not putting it there allows us
26
- # to have a unified inspect method
27
- @unsaved_values = Set.new
28
- @transient_values = Set.new
29
- @values[:id] = id if id
30
- end
31
-
32
- def self.construct_from(values, api_key=nil)
33
- obj = self.new(values[:id], api_key)
34
- obj.refresh_from(values, api_key)
35
- obj
36
- end
37
-
38
- def to_s(*args)
39
- Rainforest::JSON.dump(@values, :pretty => true)
40
- end
41
-
42
- def inspect()
43
- id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
44
- "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + Rainforest::JSON.dump(@values, :pretty => true)
45
- end
46
-
47
- def refresh_from(values, api_key, partial=false)
48
- @api_key = api_key
49
-
50
- @previous_metadata = values[:metadata]
51
- removed = partial ? Set.new : Set.new(@values.keys - values.keys)
52
- added = Set.new(values.keys - @values.keys)
53
-
54
- instance_eval do
55
- remove_accessors(removed)
56
- add_accessors(added)
57
- end
58
- removed.each do |k|
59
- @values.delete(k)
60
- @transient_values.add(k)
61
- @unsaved_values.delete(k)
62
- end
63
- values.each do |k, v|
64
- @values[k] = Util.convert_to_rainforest_object(v, api_key)
65
- @transient_values.delete(k)
66
- @unsaved_values.delete(k)
67
- end
68
- end
69
-
70
- def [](k)
71
- @values[k.to_sym]
72
- end
73
-
74
- def []=(k, v)
75
- send(:"#{k}=", v)
76
- end
77
-
78
- def keys
79
- @values.keys
80
- end
81
-
82
- def values
83
- @values.values
84
- end
85
-
86
- def to_json(*a)
87
- Rainforest::JSON.dump(@values)
88
- end
89
-
90
- def as_json(*a)
91
- @values.as_json(*a)
92
- end
93
-
94
- def to_hash
95
- @values
96
- end
97
-
98
- def each(&blk)
99
- @values.each(&blk)
100
- end
101
-
102
- protected
103
-
104
- def metaclass
105
- class << self; self; end
106
- end
107
-
108
- def remove_accessors(keys)
109
- metaclass.instance_eval do
110
- keys.each do |k|
111
- next if @@permanent_attributes.include?(k)
112
- k_eq = :"#{k}="
113
- remove_method(k) if method_defined?(k)
114
- remove_method(k_eq) if method_defined?(k_eq)
115
- end
116
- end
117
- end
118
-
119
- def add_accessors(keys)
120
- metaclass.instance_eval do
121
- keys.each do |k|
122
- next if @@permanent_attributes.include?(k)
123
- k_eq = :"#{k}="
124
- define_method(k) { @values[k] }
125
- define_method(k_eq) do |v|
126
- if v == ""
127
- raise ArgumentError.new(
128
- "You cannot set #{k} to an empty string." +
129
- "We interpret empty strings as nil in requests." +
130
- "You may set #{self}.#{k} = nil to delete the property.")
131
- end
132
- @values[k] = v
133
- @unsaved_values.add(k)
134
- end
135
- end
136
- end
137
- end
138
-
139
- def method_missing(name, *args)
140
- # TODO: only allow setting in updateable classes.
141
- if name.to_s.end_with?('=')
142
- attr = name.to_s[0...-1].to_sym
143
- add_accessors([attr])
144
- begin
145
- mth = method(name)
146
- rescue NameError
147
- raise NoMethodError.new("Cannot set #{attr} on this object. HINT: you can't set: #{@@permanent_attributes.to_a.join(', ')}")
148
- end
149
- return mth.call(args[0])
150
- else
151
- return @values[name] if @values.has_key?(name)
152
- end
153
-
154
- begin
155
- super
156
- rescue NoMethodError => e
157
- if @transient_values.include?(name)
158
- raise NoMethodError.new(e.message + ". HINT: The '#{name}' attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Rainforest's API, probably as a result of a save(). The attributes currently available on this object are: #{@values.keys.join(', ')}")
159
- else
160
- raise
161
- end
162
- end
163
- end
164
- end
165
- end
@@ -1,8 +0,0 @@
1
- module Rainforest
2
- class Run < APIResource
3
- include Rainforest::APIOperations::Create
4
- include Rainforest::APIOperations::Delete
5
- include Rainforest::APIOperations::Update
6
- include Rainforest::APIOperations::List
7
- end
8
- end
@@ -1,20 +0,0 @@
1
- module Rainforest
2
- class SingletonAPIResource < APIResource
3
- def self.url()
4
- if self == SingletonAPIResource
5
- raise NotImplementedError.new('SingletonAPIResource is an abstract class. You should perform actions on its subclasses (Account, etc.)')
6
- end
7
- "/v1/#{CGI.escape(class_name.downcase)}"
8
- end
9
-
10
- def url
11
- self.class.url
12
- end
13
-
14
- def self.retrieve(api_key=nil)
15
- instance = self.new(nil, api_key)
16
- instance.refresh
17
- instance
18
- end
19
- end
20
- end
@@ -1,100 +0,0 @@
1
- module Rainforest
2
- module Util
3
- def self.objects_to_ids(h)
4
- case h
5
- when APIResource
6
- h.id
7
- when Hash
8
- res = {}
9
- h.each { |k, v| res[k] = objects_to_ids(v) unless v.nil? }
10
- res
11
- when Array
12
- h.map { |v| objects_to_ids(v) }
13
- else
14
- h
15
- end
16
- end
17
-
18
- def self.object_classes
19
- @object_classes ||= {
20
- 'Run' => Run,
21
- 'list' => ListObject
22
- }
23
- end
24
-
25
- # TODO(jon): Suggest an object attribute be returned instead of this.
26
- def self.convert_to_rainforest_object(resp, api_key)
27
- case resp
28
- when Array
29
- resp.map { |i| convert_to_rainforest_object(i, api_key) }
30
- when Hash
31
- # Try converting to a known object class. If none available, fall back to generic RainforestObject
32
- object_classes.fetch(resp[:object], RainforestObject).construct_from(resp, api_key)
33
- else
34
- resp
35
- end
36
- end
37
-
38
- def self.file_readable(file)
39
- # This is nominally equivalent to File.readable?, but that can
40
- # report incorrect results on some more oddball filesystems
41
- # (such as AFS)
42
- begin
43
- File.open(file) { |f| }
44
- rescue
45
- false
46
- else
47
- true
48
- end
49
- end
50
-
51
- def self.symbolize_names(object)
52
- case object
53
- when Hash
54
- new = {}
55
- object.each do |key, value|
56
- key = (key.to_sym rescue key) || key
57
- new[key] = symbolize_names(value)
58
- end
59
- new
60
- when Array
61
- object.map { |value| symbolize_names(value) }
62
- else
63
- object
64
- end
65
- end
66
-
67
- def self.url_encode(key)
68
- URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
69
- end
70
-
71
- def self.flatten_params(params, parent_key=nil)
72
- result = []
73
- params.each do |key, value|
74
- calculated_key = parent_key ? "#{parent_key}[#{url_encode(key)}]" : url_encode(key)
75
- if value.is_a?(Hash)
76
- result += flatten_params(value, calculated_key)
77
- elsif value.is_a?(Array)
78
- result += flatten_params_array(value, calculated_key)
79
- else
80
- result << [calculated_key, value]
81
- end
82
- end
83
- result
84
- end
85
-
86
- def self.flatten_params_array(value, calculated_key)
87
- result = []
88
- value.each do |elem|
89
- if elem.is_a?(Hash)
90
- result += flatten_params(elem, calculated_key)
91
- elsif elem.is_a?(Array)
92
- result += flatten_params_array(elem, calculated_key)
93
- else
94
- result << ["#{calculated_key}[]", elem]
95
- end
96
- end
97
- result
98
- end
99
- end
100
- end