stretchr 1.0.0 → 1.2.2

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: eba1f84e305e4aa0140195449c53abe1513d309b
4
+ data.tar.gz: 5bb66d1eefbf3cace2b220c70871d255fa38955e
5
+ SHA512:
6
+ metadata.gz: 6b755e5410cb25f0781521be2590484768b81157c74acef767e5bd9abd5b4331c651dff28233cc30dca97ec3e4f37f7b19f0a754368e7f95732315cb24a666bd
7
+ data.tar.gz: 54e43e6a35302103c18cbc5c6181db05d613f2a43d7383512cbf279985d5bfcdb5d07df8b4f6771f4da0149b11b2aee113e8701da30db5529c1c9895de1df8d8
@@ -0,0 +1,52 @@
1
+ require "uri"
2
+
3
+ module Stretchr
4
+ # Bag is a general parameter holder with
5
+ # special functions for converting a hash into
6
+ # a query string for Stretchr
7
+ class Bag
8
+ def initialize(options = {})
9
+ @params = {} # the general param hash
10
+ @prefix = options[:prefix] # the prefix added to every key before saving
11
+ end
12
+
13
+ # Set will store a value for a given key
14
+ # ==== Parameters
15
+ # +param+ - The key to store the value under
16
+ # +value+ - The value to store under that key, will always overwrite, so send an array for multiples
17
+ #
18
+ # ==== Examples
19
+ # s = Stretchr::Bag.new
20
+ # s.set("key", "value")
21
+ # s.get("key") #=> "value"
22
+ def set(param, value)
23
+ param = "#{@prefix}#{param}" if @prefix
24
+ @params[param] = value
25
+ end
26
+
27
+ # Get will retrieve a stored value
28
+ # ==== Parameters
29
+ # +param+ - The key to retrieve the values for
30
+ #
31
+ # ==== Examples
32
+ # s = Stretchr::Bag.new
33
+ # s.set("key", "value")
34
+ # s.get("key") #=> "value"
35
+ def get(param)
36
+ param = "#{@prefix}#{param}" if @prefix
37
+ @params[param]
38
+ end
39
+
40
+ # Returns a url encoded query string of the current stored
41
+ # values
42
+ #
43
+ # ==== Examples
44
+ # s = Stretchr::Bag.new
45
+ # s.set("key", "value")
46
+ # s.set("key2", ["value1", "value2"])
47
+ # s.query_string #=> "key=value&key2=value1&key2=value2"
48
+ def query_string
49
+ URI.encode_www_form(@params)
50
+ end
51
+ end
52
+ end
@@ -1,201 +1,34 @@
1
1
  module Stretchr
2
-
3
2
  class Client
3
+ attr_accessor :transporter, :api_version, :project, :key, :hostname
4
+ # Initializes a new stretchr client
5
+ # This is the main entrypoint into working with stretchr
6
+ #
7
+ # ==== Parameters
8
+ # +options[:project]+ - The project you're working with
9
+ # +options[:key]+ - The key for the project you want to use
10
+ # +options[:transporter]+ - The transporter to use for actually making requests
11
+ # +options[:api_version]+ - The stretchr API endpoint we want to communicate with
4
12
  def initialize(options = {})
5
- options ||= {}
6
-
7
- options[:project] ||= Stretchr.configuration.project
8
- options[:private_key] ||= Stretchr.configuration.private_key
9
- options[:public_key] ||= Stretchr.configuration.public_key
10
- options[:noisy_errors] ||= (Stretchr.configuration.noisy_errors || false)
11
- # check for required arguments
12
- [:project, :public_key, :private_key].each do | required_option |
13
- raise MissingAttributeError, "#{required_option} is required." unless options[required_option]
14
- end
15
-
16
- options.each do |name, value|
17
- send("#{name}=", value)
18
- end
19
-
20
- # create defaults if the user didn't specify anything
21
- @signatory ||= Stretchr::Signatory
22
- @transporter ||= Stretchr::DefaultTransporter.new
23
- @version ||= "v1"
24
- @path ||= ""
25
- @query = {}
26
-
27
- end
28
-
29
- attr_accessor :project, :private_key, :public_key, :path, :http_method, :http_body, :version, :transporter, :signatory, :noisy_errors
30
-
31
- #-------------------HTTP Actions-----------------
32
-
33
- # generate_request makes a Stretchr::Request based on the current settings
34
- # in this Stretchr object.
35
- def generate_request
36
- Stretchr::Request.new(
37
- :http_method => http_method,
38
- :signed_uri => signed_uri,
39
- :body => http_body
40
- )
41
- end
42
-
43
- def make_request!
44
- # create and make the request
45
- response = self.transporter.make_request(generate_request)
46
- raise_errors_in_response(response) if noisy_errors
47
- response
48
- end
49
-
50
- def raise_errors_in_response(response)
51
- if [404, 500, 401, 403, 406, 400].include?(response.status)
52
- raise_error(response.status)
53
- end
54
- end
55
-
56
- def raise_error(status)
57
- case status
58
- when 404
59
- raise NotFound
60
- when 500
61
- raise InternalServerError
62
- when 401
63
- raise Unauthorized
64
- when 403
65
- raise Forbidden
66
- when 400
67
- raise BadRequest
68
- else
69
- raise Unknown
70
- end
71
- end
72
-
73
- # get performs a GET request and returns the Stretchr::Response.
74
- def get
75
-
76
- self.http_method = :get
77
- make_request!
78
-
79
- end
80
-
81
- # post performs a POST and returns a Stretch::Response
82
- def post
83
- self.http_method = :post
84
- make_request!
85
- end
86
-
87
- def put
88
- self.http_method = :put
89
- make_request!
90
- end
91
-
92
- def delete
93
- self.http_method = :delete
94
- make_request!
95
- end
96
-
97
- #---------------- Friendly Actions --------------
98
-
99
- def create(object)
100
- self.body(object).post
101
- end
102
-
103
- def replace(object)
104
- self.body(object).post
105
- end
106
-
107
- def update(object)
108
- self.body(object).put
109
- end
110
-
111
- def read
112
- self.get
113
- end
114
-
115
- #----------------Friendly Functions--------------
116
- def url
117
- uri.to_s
118
- end
119
-
120
- def to_url
121
- url
122
- end
123
-
124
- def uri
125
- URI::HTTP.build(host: "#{project}.stretchr.com", query: merge_query, path: merge_path)
126
- end
127
-
128
- def signed_uri
129
- Stretchr::Signatory.generate_signed_url(http_method, uri, public_key, private_key, http_body)
130
- end
131
-
132
- #---------------Parameter Building---------------
133
-
134
- def order(parameters)
135
- @query["~order"] = parameters.to_s
136
- self
137
- end
138
-
139
- def skip(parameters)
140
- @query["~skip"] = parameters.to_i
141
- self
142
- end
143
-
144
- def limit(parameters)
145
- @query["~limit"] = parameters.to_i
146
- self
147
- end
148
-
149
- def page(parameters)
150
- skip((@query["~limit"] * parameters.to_i) - @query["~limit"])
151
- self
152
- end
153
-
154
- def parameters(parameters)
155
- @query.merge!(parameters)
156
- self
157
- end
158
-
159
- def body(body_params)
160
- self.http_body = body_params.to_json
161
-
162
- self
163
- end
164
-
165
- def where(params)
166
- params.each do |key, value|
167
- @query["\:#{key.to_s}"] = value
168
- end
169
- self
170
- end
171
-
172
- #-----------------Basic Routing-----------------
173
-
13
+ self.transporter = options[:transporter] || Stretchr::JSONTransporter.new
14
+ self.api_version = options[:api_version] || Stretchr.config["api_version"]
15
+ self.project = options[:project]
16
+ self.key = options[:key]
17
+ self.hostname = options[:hostname] || Stretchr.config["hostname"]
18
+ end
19
+
20
+ # Catches everything you can throw at client and passes it on to a new request object
21
+ # All interactions will then take place on the request object itself
22
+ # This is the primary function of the Client class, to get you into
23
+ # a unique request as quickly as possible
24
+ #
25
+ # ==== Example
26
+ # stretchr = Stretchr::Client.new({project: "project", key: "key"})
27
+ # stretchr.people(1).cars.path # => people/1/cars
174
28
  def method_missing(method, *args)
175
- add_collection(method, *args)
176
- end
177
-
178
- def add_collection(collection, id = nil)
179
- @path += "/#{collection}"
180
- if id
181
- id.gsub!(/[^0-9A-Za-z.\-]/, '_') if id.is_a?(String) #remove non-ascii
182
- @path += "/#{id}"
183
- end
184
- self
29
+ r = Stretchr::Request.new({client: self})
30
+ r.param("key", key) if key
31
+ r.send(method, *args)
185
32
  end
186
-
187
- private
188
-
189
- def merge_query
190
- unless @query == nil || @query == {}
191
- URI.encode_www_form(@query)
192
- end
193
- end
194
-
195
- def merge_path
196
- "/api/#{version}" + @path
197
- end
198
-
199
33
  end
200
-
201
34
  end
@@ -0,0 +1,8 @@
1
+ api_version: v1.1
2
+ hostname: stretchr.com
3
+ v1.1:
4
+ status: ~status
5
+ data: ~data
6
+ errors: ~errors
7
+ changes: ~changes
8
+ items: ~items
@@ -0,0 +1,194 @@
1
+ require "uri"
2
+
3
+ module Stretchr
4
+ # The request class is where most of the url building magic takes place
5
+ # It collects all methods passed to it and uses them to build a url for you
6
+ class Request
7
+ attr_accessor :client
8
+ def initialize(options = {})
9
+ # Pass in the client so we get all the options required
10
+ self.client = options[:client]
11
+
12
+ # URL BUILDING
13
+ # This is where we'll initialize everything needed to build up the urls
14
+ @path_elements = []
15
+ @params = Stretchr::Bag.new # a bag to hold the params for th url building
16
+ @filters = Stretchr::Bag.new({prefix: ":"}) # a bag to hold the filters
17
+ end
18
+
19
+ # Returns the current path that the request is working with
20
+ #
21
+ # ==== Examples
22
+ # r = Stretchr::Request.new
23
+ # r.people(1).cars.path #=> "people/1/cars"
24
+ def path
25
+ @path_elements.join("/")
26
+ end
27
+
28
+ # Returns the full url, including the path for the current request
29
+ def to_url
30
+ to_uri.to_s
31
+ end
32
+
33
+ # Builds a URI object
34
+ def to_uri
35
+ URI::HTTP.build(host: merge_host, path: merge_path, query: merge_query)
36
+ end
37
+
38
+ # Set params for the url
39
+ #
40
+ # ==== Examples
41
+ # r = Stretchr::Request.new
42
+ # r.param("key", "value")
43
+ def param(key, value)
44
+ @params.set(key, value)
45
+ return self
46
+ end
47
+
48
+ # Set filters for the url
49
+ #
50
+ # ==== Examples
51
+ # r = Stretchr::Request.new
52
+ # r.where("key", "value")
53
+ def where(key, value)
54
+ @filters.set(key, value)
55
+ return self
56
+ end
57
+
58
+ # Sets the maximum number of items you want to get back
59
+ #
60
+ # ==== Examples
61
+ # r = Stretchr::Request.new
62
+ # r.accounts.limit(10).get #=> Returns 10 accounts
63
+ def limit(value)
64
+ param("limit", value)
65
+ end
66
+
67
+ # Tell stretchr to skip some items in the response
68
+ #
69
+ # ==== Examples
70
+ # r = Stretchr::Request.new
71
+ # r.accounts.limit(10).skip(10).get #=> Returns 10 accounts, starting at 11
72
+ def skip(value)
73
+ param("skip", value)
74
+ end
75
+
76
+ # Convenience method for using pages instead of skip values
77
+ # defaults to 1000 if no limit is known yet
78
+ #
79
+ # ==== Examples
80
+ # r = Stretchr::Request.new
81
+ # r.accounts.limit(10).page(2).get #=> Returns 10 accounts, starting at 11
82
+ def page(value)
83
+ l = @params.get("limit") || 1000
84
+ skip((l.to_i * value.to_i) - l.to_i)
85
+ end
86
+
87
+ # Set the order of the response
88
+ #
89
+ # ==== Examples
90
+ # r = Stretchr::Request.new
91
+ # r.accounts.order("-name").get #=> Orders accounts by name, descending
92
+ def order(value)
93
+ param("order", value)
94
+ end
95
+
96
+ # Performs a GET for the current request
97
+ # Returns a Stretchr::Response object
98
+ #
99
+ # ==== Examples
100
+ # r = Stretchr::Request.new
101
+ # r.people.get #=> Stretchr::Response object
102
+ def get
103
+ self.make_request!({uri: self.to_uri, method: :get})
104
+ end
105
+ alias :read :get
106
+
107
+ # Performs a POST to create a new resource
108
+ # Returns a Stretchr::Response object
109
+ #
110
+ # ==== Examples
111
+ # r = Stretchr::Request.new
112
+ # r.people.create({name: "ryan"}) #=> Stretchr::Response object
113
+ def create(body)
114
+ self.make_request!({uri: self.to_uri, method: :post, body: body})
115
+ end
116
+ alias :post :create
117
+
118
+ # Performs a PUT to replace an object
119
+ # Returns a Stretchr::Response object
120
+ #
121
+ # ==== Examples
122
+ # r = Stretchr::Request.new
123
+ # r.people.replace({name: "ryan"}) #=> Stretchr::Response object
124
+ def replace(body)
125
+ self.make_request!({uri: self.to_uri, method: :put, body: body})
126
+ end
127
+ alias :put :replace
128
+
129
+ # Performs a PATCH to update an object
130
+ # will not delete non-included fields
131
+ # just update included ones
132
+ # Returns a Stretchr::Response object
133
+ #
134
+ # ==== Examples
135
+ # r = Stretchr::Request.new
136
+ # r.people.update({name: "ryan"}) #=> Stretchr::Response object
137
+ def update(body)
138
+ self.make_request!({uri: self.to_uri, method: :patch, body: body})
139
+ end
140
+ alias :patch :update
141
+
142
+ # Performs a DELETE to remove an object
143
+ # deletes an object or entire collection
144
+ # SERIOUSLY - THIS DELETES THINGS
145
+ #
146
+ # ==== Examples
147
+ # r = Stretchr::Request.new
148
+ # r.people.remove #=> Stretchr::Response object
149
+ def remove
150
+ self.make_request!({uri: self.to_uri, method: :delete})
151
+ end
152
+ alias :delete :remove
153
+
154
+ # Actually sends the request to the transporter
155
+ def make_request!(options = {})
156
+ options[:client] = client
157
+ self.client.transporter.make_request(options)
158
+ end
159
+
160
+
161
+ # Catch everyting not defined and turn it into url parameters
162
+ # If you include an argument, it will be passed into the url as the ID for the
163
+ # collection you specified in the method name
164
+ #
165
+ # ==== Examples
166
+ # r = Stretchr::Request.new
167
+ # r.people(1).cars.path #=> "people/1/cars"
168
+ def method_missing(method, *args)
169
+ @path_elements << method.to_s
170
+ @path_elements << args[0] if args[0]
171
+ return self
172
+ end
173
+
174
+ private
175
+
176
+ # Merges the path from the @path_elements
177
+ def merge_path
178
+ "/api/#{client.api_version}/#{@path_elements.join('/')}"
179
+ end
180
+
181
+ # Merges query params and filter params
182
+ def merge_query
183
+ p = []
184
+ p << @params.query_string unless @params.query_string == ""
185
+ p << @filters.query_string unless @filters.query_string == ""
186
+ return p.size > 0 ? p.join("&") : nil
187
+ end
188
+
189
+ # Generate the host from the hostname and project
190
+ def merge_host
191
+ "#{client.project}.#{client.hostname}"
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,30 @@
1
+ require "json"
2
+ module Stretchr
3
+ # Response parser for stretchr
4
+ # ==== Parameters
5
+ # +response+ - The raw response from stretchr
6
+ # +options[:api_version]+ - The version of the api we're talking to, tells us how to parse
7
+ class Response
8
+ attr_reader :raw, :parsed, :data, :api_version, :errors, :changes, :status, :items
9
+ def initialize(response, options = {})
10
+ @api_version = options[:api_version] || Stretchr.config["api_version"] #api version is used to determine how to parse response
11
+ @raw = response
12
+ @parsed = JSON.parse(response)
13
+ begin
14
+ @data = @parsed[Stretchr.config[@api_version]["data"]] #stretchr data object
15
+ @errors = @parsed[Stretchr.config[@api_version]["errors"]] #errors
16
+ @changes = @parsed[Stretchr.config[@api_version]["changes"]] #changes (POST/PUT/PATCH/DELETE)
17
+ @status = @parsed[Stretchr.config[@api_version]["status"]] #request status
18
+ if @data
19
+ @items = @data[Stretchr.config[@api_version]["items"]]
20
+ end
21
+ rescue
22
+ end
23
+ end
24
+
25
+ # Returns whether or not the request was successful
26
+ def success?
27
+ @status >= 200 && @status <= 299
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,47 @@
1
+ require "uri" if !defined? URI
2
+ require "net/http" if !defined? Net
3
+ module Stretchr
4
+ class JSONTransporter
5
+ # Perform the request against stretchr
6
+ # ==== Parameters
7
+ # +request+ - The request object
8
+ # Expects: {uri: URIOBJECT, body: "body", method: "PUT/PATCH/POST/DELETE/GET", client: stretchr client}
9
+ def make_request(request)
10
+ response = nil
11
+ Net::HTTP.start(request[:uri].host, request[:uri].port) do |http|
12
+ http_request = generate_request(request)
13
+ response = http.request http_request # Net::HTTPResponse object
14
+ end
15
+ if request[:client]
16
+ return Stretchr::Response.new(response.body, {:api_version => request[:client].api_version})
17
+ else
18
+ return Stretchr::Response.new(response.body)
19
+ end
20
+ end
21
+
22
+ # Generates the actual request, including the method
23
+ def generate_request(request)
24
+
25
+ request_uri = request[:uri].request_uri
26
+
27
+ case request[:method]
28
+ when :get
29
+ req = Net::HTTP::Get.new request_uri
30
+ when :post
31
+ req = Net::HTTP::Post.new request_uri, {'Content-Type' => "application/json"}
32
+ req.body = request[:body]
33
+ req
34
+ when :put
35
+ req = Net::HTTP::Put.new request_uri, {'Content-Type' => "application/json"}
36
+ req.body = request[:body]
37
+ req
38
+ when :patch
39
+ req = Net::HTTP::Patch.new request_uri, {'Content-Type' => "application/json"}
40
+ req.body = request[:body]
41
+ req
42
+ when :delete
43
+ req = Net::HTTP::Delete.new request_uri
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,25 +1,14 @@
1
1
  module Stretchr
2
- class TestTransporter
3
-
4
- attr_accessor :requests, :responses
5
-
6
- def initialize
7
- self.requests = []
8
- self.responses = []
9
- end
10
-
11
- def make_request(request)
12
-
13
- # store the request and return the next response in the local queue
14
- # not NOT actually make any http requests
15
-
16
- self.requests << request
17
-
18
- # return the response
19
- self.responses.shift
20
-
21
- end
22
-
23
-
24
- end
2
+ class TestTransporter
3
+ attr_accessor :requests, :responses
4
+ def initialize
5
+ self.requests = []
6
+ self.responses = []
7
+ end
8
+
9
+ def make_request(request)
10
+ requests << request
11
+ responses.shift
12
+ end
13
+ end
25
14
  end
data/lib/stretchr.rb CHANGED
@@ -1,32 +1,31 @@
1
- require "uri"
2
- require "cgi"
3
- require "json"
4
-
5
-
1
+ # Config File
2
+ # This file contains all the configuration for the stretchr api
3
+ # and this gem. They are namespaced by api version where appropriate
4
+ require "yaml"
6
5
  module Stretchr
7
- #exceptions that can be used by stretchr
8
- require_relative 'stretchr/exceptions'
9
-
10
- #configuration
11
- require_relative 'stretchr/configuration'
6
+ def self.config
7
+ @config ||= YAML::load_file(File.join(Dir.pwd,"lib", "stretchr", "defaults.yaml"))
8
+ end
9
+ end
12
10
 
13
- @configuration = Stretchr::Configuration.new
11
+ # The main stretchr client
12
+ require "stretchr/client"
14
13
 
15
- def self.configuration
16
- @configuration
17
- end
14
+ # The main stretchr request, this is where the url is built
15
+ require "stretchr/request"
18
16
 
19
- def self.config
20
- yield(@configuration)
21
- end
17
+ # Bags are responsible for hanging on to params while building urls
18
+ require "stretchr/bag"
22
19
 
23
- #the actual client library
24
- require_relative 'stretchr/client'
25
- require_relative 'stretchr/security'
26
- require_relative 'stretchr/transporters'
27
- require_relative 'stretchr/resources'
28
- require_relative 'stretchr/stretchr_request'
29
- require_relative 'stretchr/stretchr_response'
20
+ # Transporters
21
+ # A transporter is anything that actually makes requests
22
+ # It is a class with only one public function: make_request(request)
23
+ # you pass in a request object, it performs the request and then
24
+ # returns a response object
25
+ require "stretchr/transporters/test_transporter" # a transporter for testing, does not make real requests
26
+ require "stretchr/transporters/json_transporter" # a transporter that uses json to talk to stretchr
30
27
 
31
-
32
- end
28
+ # Response object
29
+ # The response object takes in a JSON response from stretchr
30
+ # and parses it into more easily used data
31
+ require "stretchr/response"
@@ -0,0 +1,13 @@
1
+ {
2
+ "~data":{
3
+ "~count":4,
4
+ "~items":[
5
+ {"name":"Mat","~id":"id-here"},
6
+ {"name":"Laurie","~id":"id-here"},
7
+ {"name":"Steve","~id":"id-here"},
8
+ {"name":"Chi","~id":"id-here"}
9
+ ],
10
+ "~total": 155563
11
+ },
12
+ "~status":200
13
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "~data":{
3
+ "field":"value"
4
+ },
5
+ "~status": 200
6
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "~status": 404,
3
+ "~errors": [{"~message":"one"}, {"~message":"two"}]
4
+ }