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.
@@ -1,129 +0,0 @@
1
- module Stretchr
2
- class Resource
3
-
4
- # Class Methods to find objects
5
-
6
- def self.find(params = {})
7
- stretchr = stretchr_client
8
- #FIXME : Why does this need to be duplicated?
9
- stretchr.path = prep_path(stretchr.path.dup, params)
10
- response = stretchr.get
11
- return false if !response.success?
12
- self.new(response.data)
13
- end
14
-
15
- def self.all(params = {})
16
- stretchr = stretchr_client
17
- stretchr.path = prep_path(stretchr.path.dup, params)
18
- response = stretchr.get
19
- return [] if response.data["~c"] == 0 || !response.data["~i"]
20
- response.data["~i"].map {|r| self.new(r) }
21
- end
22
-
23
- def self.where(params = {})
24
- stretchr = stretchr_client
25
- #snag the vars that are needed for the path
26
- path_vars = stretchr.path.scan(/:([a-zA-Z0-9_-]*)/i).flatten
27
- #now convert the path as best we can
28
- stretchr.path = prep_path(stretchr.path.dup, params)
29
- #now remove the path params from the request
30
- params.delete_if {|key, value| path_vars.include?(key.to_s)}
31
-
32
- response = stretchr.where(params).get
33
- #return false if nothing returned or search wasn't successful
34
- return false if !response.success? || response.data["~c"] == 0 || !response.data["~i"]
35
- response.data["~i"].map {|i| self.new(i) }
36
- end
37
-
38
- def self.stretchr_client
39
- Stretchr::Client.new(@config)
40
- end
41
-
42
- def self.create(objects = [], params = {})
43
- #convert it to an array for easy adding
44
- objects = [objects] if !objects.is_a?(Array)
45
- objects.map! {|o| setup_attributes_for_stretchr(o) }
46
- stretchr = stretchr_client
47
- stretchr.path = prep_path(stretchr.path.dup, params)
48
- stretchr.body(objects)
49
- response = stretchr.post
50
- count = 0
51
- stretchr_objects = objects.map do |o|
52
- account = self.new(o)
53
- account.parse_changes(response.changed[count])
54
- count += 1
55
- account
56
- end
57
- return stretchr_objects.first if stretchr_objects.length == 1
58
- return stretchr_objects
59
- end
60
-
61
- def self.stretchr_config(params = {})
62
- @config ||= {}
63
- @config.merge!(params)
64
- end
65
-
66
- # Instance Methods
67
-
68
- def initialize(params = {})
69
- @attributes = {}
70
- params.each {|key, value| self.send("#{key}=", value) }
71
- end
72
-
73
- def save(params = {})
74
- stretchr = self.class.stretchr_client
75
- stretchr.path = self.class.prep_path(stretchr.path.dup, params)
76
- stretchr.body(setup_attributes_for_stretchr)
77
- if self.stretchr_id
78
- response = stretchr.put
79
- else
80
- response = stretchr.post
81
- end
82
- parse_changes(response.changed)
83
- end
84
-
85
- def parse_changes(response)
86
- #FIXME : Should handle change objects and deltas here and update the object accordingly
87
- return unless response
88
- response.each {|key, value| self.send("#{key}=", value)}
89
- self
90
- end
91
-
92
- def to_hash
93
- @attributes
94
- end
95
-
96
- def to_json
97
- to_hash.to_json
98
- end
99
-
100
- def method_missing(method, *args)
101
- attribute = method.to_s
102
- if attribute =~ /=$/ #if the method name ends in an =
103
- @attributes[attribute.chop.gsub("~", "stretchr_")] = args[0]
104
- else
105
- @attributes[attribute]
106
- end
107
- end
108
-
109
- private
110
-
111
- def self.prep_path(path, params = {})
112
- params.each {|key, value| path.gsub!(":#{key.to_s}", value.to_s) unless value == nil}
113
- #remove any unchanged params from the path
114
- path.gsub!(/(:[a-zA-Z0-9_-]*)/, "")
115
- path
116
- end
117
-
118
- def setup_attributes_for_stretchr
119
- self.class.setup_attributes_for_stretchr(@attributes)
120
- end
121
-
122
- def self.setup_attributes_for_stretchr(attributes)
123
- stretchr_attributes = {}
124
- attributes.each_pair {|key, value| stretchr_attributes[key.to_s.gsub(/^stretchr_/, "~")] = value}
125
- stretchr_attributes
126
- end
127
-
128
- end
129
- end
@@ -1 +0,0 @@
1
- require_relative 'resources/resource.rb'
@@ -1,52 +0,0 @@
1
- require 'uri' unless defined? URI
2
- require 'cgi' unless defined? CGI
3
- require 'digest/sha1'
4
-
5
- module Stretchr
6
- class Signatory
7
- class << self
8
- def generate_signed_url(method, uri, public_key, private_key, body = nil)
9
-
10
- #we need a URI, let's make sure it's what we have
11
- uri = URI.parse(URI.escape(uri)) unless uri.is_a?(URI)
12
-
13
- #preparation
14
- query = CGI.parse(uri.query || "")
15
- query["~key"] = public_key
16
-
17
- #store this for later, we'll need it
18
- public_query = URI.encode_www_form(query)
19
-
20
- #now add the private stuff
21
- query["~private"] = private_key
22
- query["~bodyhash"] = Digest::SHA1.hexdigest(body) unless body == nil
23
-
24
- #sort it
25
- query = sort_query(query)
26
- uri.query = URI.encode_www_form(query)
27
-
28
- #append the method
29
- signature = generate_signature(method, uri.to_s)
30
-
31
- #now we prepare it for public use
32
- public_query = public_query + "&" unless public_query == nil
33
- uri.query = public_query + URI.encode_www_form("~sign" => signature)
34
-
35
- return uri
36
- end
37
-
38
- private
39
-
40
- def generate_signature(http_method, private_url)
41
- combined = "#{http_method.to_s.upcase}&#{CGI.unescape(private_url.to_s)}"
42
- Digest::SHA1.hexdigest(combined)
43
- end
44
-
45
- def sort_query(query)
46
- query.each do |key, value|
47
- value.sort! if value.is_a?(Array)
48
- end.sort
49
- end
50
- end
51
- end
52
- end
@@ -1 +0,0 @@
1
- require_relative 'security/signatory'
@@ -1,16 +0,0 @@
1
- module Stretchr
2
- class Request
3
-
4
- attr_accessor :http_method, :body, :signed_uri, :headers
5
-
6
- def initialize(options = {})
7
-
8
- @http_method = options[:http_method]
9
- @body = options[:body]
10
- @signed_uri = options[:signed_uri]
11
- @headers = options[:headers]
12
-
13
- end
14
-
15
- end
16
- end
@@ -1,39 +0,0 @@
1
- require 'json' unless defined? JSON
2
- module Stretchr
3
- class Response
4
-
5
- attr_reader :json_string, :json_object, :status, :client_context, :data, :changed, :errors, :raw_response
6
-
7
- def initialize(options = nil)
8
-
9
- options ||= {}
10
-
11
- @raw_response = options[:response]
12
-
13
- if options[:json]
14
-
15
- # save the original json string
16
- @json_string = options[:json]
17
- @json_object = JSON.parse(@json_string)
18
-
19
- @status = @json_object["~s"]
20
-
21
- @client_context = @json_object["~x"]
22
- @data = @json_object["~d"]
23
- @changed = @json_object["~ch"]["~deltas"] if @json_object["~ch"]
24
-
25
- unless @json_object["~e"].nil?
26
- @errors = @json_object["~e"].collect {| error_obj | error_obj["~m"] }
27
- end
28
-
29
- end
30
-
31
- end
32
-
33
- # Gets whether this is a successful response.
34
- def success?
35
- @status >= 200 && @status <= 299
36
- end
37
-
38
- end
39
- end
@@ -1,41 +0,0 @@
1
- require "uri" if !defined? URI
2
- require "net/http" if !defined? Net
3
-
4
- module Stretchr
5
- class DefaultTransporter
6
-
7
- def make_request(request)
8
- response = nil
9
-
10
- Net::HTTP.start(request.signed_uri.host, request.signed_uri.port) do |http|
11
-
12
- http_request = generate_request(request)
13
- response = http.request http_request # Net::HTTPResponse object
14
-
15
- end
16
-
17
- return Stretchr::Response.new({:response => response, :json => response.body})
18
- end
19
-
20
- def generate_request(request)
21
-
22
- request_uri = request.signed_uri.request_uri
23
-
24
- case request.http_method
25
- when :get
26
- req = Net::HTTP::Get.new request_uri
27
- when :post
28
- req = Net::HTTP::Post.new request_uri, {'Content-Type' => "application/json"}
29
- req.body = request.body
30
- req
31
- when :put
32
- req = Net::HTTP::Put.new request_uri, {'Content-Type' => "application/json"}
33
- req.body = request.body
34
- req
35
- when :delete
36
- req = Net::HTTP::Delete.new request_uri
37
- end
38
- end
39
-
40
- end
41
- end
@@ -1,2 +0,0 @@
1
- require_relative 'transporters/default_transporter.rb'
2
- require_relative 'transporters/test_transporter.rb'
@@ -1,187 +0,0 @@
1
- require 'test/unit'
2
- require 'test_helper.rb'
3
-
4
- class ResourcesTest < Test::Unit::TestCase
5
- Stretchr.config do |s|
6
- s.project = "test"
7
- s.private_key = "test"
8
- s.public_key = "test"
9
- end
10
-
11
- class Account < Stretchr::Resource
12
- stretchr_config path: "/books/:id", transporter: Stretchr::TestTransporter.new
13
-
14
- def self.load_response(response)
15
- self.stretchr_client.transporter.responses << Stretchr::Response.new({json: response})
16
- end
17
- end
18
-
19
- def test_ghost_methods
20
- resource = Stretchr::Resource.new
21
- resource.name = "Ryan"
22
- assert_equal resource.name, "Ryan", "Should be able to create any attributes for a resource"
23
- end
24
-
25
- def test_resources_build_clients
26
- assert_equal Stretchr::Resource.stretchr_client.class, Stretchr::Client, "Client should exist for Resources"
27
- assert_equal Account.stretchr_client.path, "/books/:id"
28
- end
29
-
30
- def test_strip_tildes
31
- account = Account.new({"~id" => "test"})
32
- assert_equal "test", account.stretchr_id, "Should have stripped the tilde"
33
- end
34
-
35
- def test_can_find_resources
36
- response = Stretchr::GenerateResponse.get_single_response({status: 200, data: {"~id" => "test", name: "Ryan"}})
37
- Account.load_response(response)
38
- account = Account.find({:id => "test"})
39
- assert_equal Account, account.class, "Should have returned an account object"
40
- assert_equal "Ryan", account.name, "Should have returned the data attributes as methods"
41
- end
42
-
43
- def test_can_get_all_resources
44
- response = Stretchr::GenerateResponse.get_collection_response({objects: [{name: "Ryan", "~id" => "ryan"}, {name: "Tim", "~id" => "tim"}]})
45
- Account.load_response(response)
46
- accounts = Account.all
47
- assert_equal "/books/", Account.stretchr_client.transporter.requests.last.signed_uri.path.gsub(/\/api\/v[0-9]*/, "") #gsub to trim out the /api/v1 bit
48
- assert_equal accounts.first.class, Account, "Should have returned an array of account objects"
49
- assert_equal accounts.first.name, "Ryan", "Should have returned the data for each object"
50
- end
51
-
52
- def test_single_from_all
53
- response = Stretchr::GenerateResponse.get_collection_response({objects: [{name: "Ryan", "~id" => "ryan"}]})
54
- Account.load_response(response)
55
- accounts = Account.all
56
- assert accounts.is_a?(Array), "Should return an array even for a single response"
57
- end
58
-
59
- def test_none_from_all
60
- response = Stretchr::GenerateResponse.get_collection_response
61
- Account.load_response(response)
62
- accounts = Account.all
63
- assert accounts.is_a?(Array), "Should return an array even for zero objects"
64
- end
65
-
66
- def test_save_new_object
67
- account = Account.new
68
- account.name = "Ryan"
69
-
70
- response = Stretchr::GenerateResponse.post_response({deltas: {"~id" => "asdf"}})
71
- Account.load_response(response)
72
-
73
- account.save
74
- assert_equal ({name: "Ryan"}).to_json, Account.stretchr_client.transporter.requests.last.body, "Should have set the correct body"
75
- assert account.stretchr_id != nil, "Should have returned and set a stretchr id"
76
- end
77
-
78
- def test_save_new_object_with_id
79
- account = Account.new
80
- account.name = "Ryan"
81
- account.stretchr_id = "ryan"
82
-
83
- response = Stretchr::GenerateResponse.post_response
84
- Account.load_response(response)
85
-
86
- account.save
87
- assert JSON.parse(Account.stretchr_client.transporter.requests.last.body).has_key?("~id"), "Should have set the stretchr id for me!"
88
- end
89
-
90
- def test_save_old_project
91
- response = Stretchr::GenerateResponse.get_single_response({status: 200, data: {"~id" => "test", name: "Ryan"}})
92
- Account.load_response(response)
93
- account = Account.find({id: "test"})
94
-
95
- account.name = "Ryan"
96
-
97
- response = Stretchr::GenerateResponse.post_response
98
- Account.load_response(response)
99
- account.save
100
-
101
- assert Account.stretchr_client.transporter.requests.last.http_method == :put, "Should have sent a put to update stretchr object"
102
-
103
- end
104
-
105
- def test_create_method
106
- response = Stretchr::GenerateResponse.post_response({deltas: [{"~id" => "asdf"}]})
107
- Account.load_response(response)
108
-
109
- account = Account.create({name: "Ryan"})
110
- assert_equal "Ryan", account.name, "Should have created the object with the attributes"
111
- assert_equal "asdf", account.stretchr_id, "Should have given it a stretchr ID from the response"
112
-
113
- response = Stretchr::GenerateResponse.post_response({deltas: [{"~id" => "ryan"}, {"~id" => "tim"}]})
114
- Account.load_response(response)
115
-
116
- accounts = Account.create([{name: "Ryan"}, {name: "Tim"}])
117
- assert_equal "ryan", accounts.first.stretchr_id, "Should have returned an array of objects"
118
- assert_equal "tim", accounts[1].stretchr_id, "Second object should work!"
119
- end
120
-
121
- def test_create_with_id
122
- response = Stretchr::GenerateResponse.post_response({deltas: [{"~id" => "asdf"}]})
123
- Account.load_response(response)
124
-
125
- account = Account.create({name: "Ryan", stretchr_id: "ryan-id"})
126
-
127
- assert Account.stretchr_client.transporter.requests.last.body.include?("~id"), "Should have set the ~id for stretchr"
128
- end
129
-
130
- def test_not_found
131
- response = Stretchr::GenerateResponse.get_single_response({status: 404})
132
- Account.load_response(response)
133
- account = Account.find({id: "ryan"})
134
- assert_equal false, account, "Should have returned false if object not found"
135
- end
136
-
137
- def test_find_resources
138
- response = Stretchr::GenerateResponse.get_collection_response({objects: [{name: "Ryan", "~id" => "ryan"}, {name: "Ryan", "~id" => "tim"}]})
139
- Account.load_response(response)
140
- accounts = Account.where("name" => "Ryan")
141
- assert accounts.length == 2, "should have returned two objects"
142
- assert accounts.first.is_a?(Account), "should have returned an array of account objects"
143
- assert accounts.first.name == "Ryan", "should have returned editable objects"
144
- end
145
-
146
- def test_find_no_resources
147
- response = Stretchr::GenerateResponse.get_collection_response
148
- Account.load_response(response)
149
- accounts = Account.where("name" => "Ryan")
150
- assert_equal false, accounts, "Should have returned false for no objects!"
151
- end
152
-
153
- def test_remove_params_from_path
154
- response = Stretchr::GenerateResponse.get_collection_response({objects: [], in_response: 0})
155
- Account.load_response(response)
156
- accounts = Account.where({id: "test"})
157
- last_uri = Account.stretchr_client.transporter.requests.last.signed_uri
158
- assert last_uri.path.include?("/books/test"), "Should have set the path"
159
- assert !URI.decode(last_uri.query).include?(":id"), "Should not have included id search in query"
160
- end
161
-
162
- def test_nil_path_param
163
- assert_nothing_raised "Nil attributes shouldn't raise error" do
164
- Stretchr::Resource.instance_eval { prep_path("/accounts/:account_id", {account_id: nil}) }
165
- end
166
- end
167
-
168
- def test_number_for_param
169
- assert_nothing_raised "Nil attributes shouldn't raise error" do
170
- Stretchr::Resource.instance_eval { prep_path("/accounts/:account_id", {account_id: 123}) }
171
- end
172
- end
173
-
174
- def test_to_json
175
- account = Account.new
176
- account.name = "Ryan"
177
- account.stretchr_id = "asdf"
178
- hash = {"name" => "Ryan", "stretchr_id" => "asdf"}
179
- assert_equal hash, account.to_hash, "Should have returned a hash"
180
- assert_equal hash.to_json, account.to_json, "Should have returned json"
181
- end
182
-
183
- #FIXME : It needs to know when an item already exists and when it's being created for the first time and handle them appropriately
184
- #FIXME : It needs to be able to throw errors for 404, etc...
185
- #FIXME : Should test "where" with params in the path as well. It should add them to the path but remove them from the query
186
-
187
- end
@@ -1,67 +0,0 @@
1
- require 'test/unit'
2
- require 'test_helper.rb'
3
-
4
- class ResourcesTest < Test::Unit::TestCase
5
-
6
- def test_signed_uri
7
-
8
- stretchr = test_stretchr_object
9
- stretchr.people(123).books
10
-
11
- assert_equal(true, stretchr.signed_uri.validate_param_presence("~sign"), "~sign param expected")
12
-
13
- end
14
-
15
- def test_signing
16
- public_key = "ABC123"
17
- private_key = "ABC123-private"
18
- body = "body"
19
- url = "http://test.stretchr.com/api/v1?:name=!Mat&:name=!Laurie&:age=>20"
20
-
21
- #as per documentation
22
- assert_equal true, Stretchr::Signatory.generate_signed_url("get", url, public_key, private_key, body).validate_param_value("~sign", "6c3dc03b3f85c9eb80ed9e4bd21e82f1bbda5b8d"), "URL signature didn't match expected"
23
- end
24
-
25
- def test_signing_again
26
- public_key = "ABC123"
27
- private_key = "PRIVATE"
28
- url = "http://localhost:8080/api/v1?:name=!Mat&:name=!Laurie&:age=>20"
29
-
30
- #as per documentation
31
- assert_equal "6841830bf612b03864edeebf9b99b7f48a8edf2d", Stretchr::Signatory.generate_signed_url("get", url, public_key, private_key).get_param("~sign").first, "URL signature didn't match expected"
32
-
33
- end
34
-
35
- def test_signing_with_no_query
36
- public_key = "ABC123"
37
- private_key = "ABC123-private"
38
- body = "body"
39
- url = "http://test.stretchr.com/api/v1"
40
-
41
- #as per documentation
42
- assert_equal "d5e1dcbba794be7dc6767076bd4747b51837f21d", Stretchr::Signatory.generate_signed_url("get", url, public_key, private_key, body).get_param("~sign").first, "URL signature didn't match expected"
43
- end
44
-
45
- def test_signing_with_escapable_characters
46
-
47
- public_key = "ABC123"
48
- private_key = "PRIVATE"
49
- url = "http://localhost:8080/api/v1/people?:~created=>10000000"
50
-
51
- #as per documentation
52
- assert_equal "b54b16d3542a1497bf22c4f61aa935e81032e7b5", Stretchr::Signatory.generate_signed_url("get", url, public_key, private_key).get_param("~sign").first, "URL signature didn't match expected"
53
-
54
- end
55
-
56
- def test_private_key_and_body_hash_removal
57
- #we shouldn't see the private key or body hash in the final url
58
- public_key = "ABC123"
59
- private_key = "ABC123-private"
60
- body = "body"
61
- url = "http://test.stretchr.com/api/v1?:name=!Mat&:name=!Laurie&:age=>20"
62
-
63
- assert_equal false, Stretchr::Signatory.generate_signed_url("get", url, public_key, private_key, body).validate_param_presence("~private"), "private param included"
64
- assert_equal false, Stretchr::Signatory.generate_signed_url("get", url, public_key, private_key, body).validate_param_presence("~bodyhash"), "private param included"
65
- end
66
-
67
- end