stretchr 1.0.0 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
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
+ }