typhoid 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .DS_STORE
4
+ .bundle
5
+ .config
6
+ coverage
7
+ InstalledFiles
8
+ lib/bundler/man
9
+ pkg
10
+ rdoc
11
+ spec/reports
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
data/.rvmrc ADDED
@@ -0,0 +1,5 @@
1
+ rvm --create ruby-1.9.3-p125@typhoid
2
+ # Ensure that Bundler is installed, install it if it is not.
3
+ if ! command -v bundle > /dev/null; then
4
+ gem install bundler
5
+ fi
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake'
4
+
5
+ # Specify your gem's dependencies in typhoid.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ typhoid (0.0.1)
5
+ typhoeus
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.1.3)
11
+ ffi (1.0.11)
12
+ json_pure (1.7.3)
13
+ mime-types (1.18)
14
+ rake (0.9.2.2)
15
+ rspec (2.10.0)
16
+ rspec-core (~> 2.10.0)
17
+ rspec-expectations (~> 2.10.0)
18
+ rspec-mocks (~> 2.10.0)
19
+ rspec-core (2.10.1)
20
+ rspec-expectations (2.10.0)
21
+ diff-lcs (~> 1.1.3)
22
+ rspec-mocks (2.10.1)
23
+ typhoeus (0.4.2)
24
+ ffi (~> 1.0)
25
+ mime-types (~> 1.18)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ json_pure (>= 1.4.1)
32
+ rake
33
+ rspec
34
+ typhoid!
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Doug Rohde
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # This Repository Has Moved
2
+
3
+ No new commits will be made here. Visit [sportngin/typhoid](https://github.com/sportngin/typhoid) for the latest work on this project.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ namespace :spec do
8
+ RSpec::Core::RakeTask.new(:docs) do |t|
9
+ t.rspec_opts = ["--format doc"]
10
+ end
11
+ end
12
+
13
+ task :default => :spec
@@ -0,0 +1,76 @@
1
+ module Typhoid
2
+ module Attributes
3
+ def load_values(params = {})
4
+ @attributes = Hash[params.map { |key, value| [key.to_s, value] }]
5
+ end
6
+
7
+ def attributes
8
+ @attributes ||= {}
9
+ @attributes
10
+ end
11
+
12
+ def read_attribute(name)
13
+ attributes[name.to_s]
14
+ end
15
+ alias :[] :read_attribute
16
+
17
+ def after_build(response, exception = nil)
18
+ assign_request_error(exception) if !response.success? || !exception.nil?
19
+ end
20
+
21
+ def assign_request_error(exception = nil)
22
+ self.resource_exception = exception || StandardError.new("Could not retrieve data from remote service")
23
+ end
24
+ private :assign_request_error
25
+
26
+ def self.included(base)
27
+ base.extend(ClassMethods)
28
+ end
29
+
30
+ protected
31
+
32
+ module ClassMethods
33
+ def field(*field_names)
34
+ raise ArgumentError, "Must specify at least one field" if field_names.length == 0
35
+ @auto_init_fields ||= []
36
+ field_names.each do |field_name|
37
+ define_accessor field_name
38
+ @auto_init_fields << field_name.to_sym
39
+ end
40
+ end
41
+
42
+ def define_accessor(field_name)
43
+ define_method field_name do
44
+ attributes[field_name.to_s]
45
+ end
46
+ define_method "#{field_name}=" do |new_value|
47
+ attributes[field_name.to_s] = new_value
48
+ end
49
+ end
50
+ private :define_accessor
51
+
52
+ def auto_init_fields
53
+ @auto_init_fields || []
54
+ end
55
+
56
+ def builder
57
+ Builder
58
+ end
59
+
60
+ def parser
61
+ Parser
62
+ end
63
+
64
+ def build(klass, response)
65
+ builder.call(klass, response)
66
+ end
67
+
68
+ def load_values(object, response)
69
+ object.tap { |obj|
70
+ obj.load_values(parser.call response.body)
71
+ obj.after_build response
72
+ }
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,58 @@
1
+ module Typhoid
2
+ class Builder
3
+ attr_reader :klass
4
+ attr_reader :response
5
+ attr_reader :body
6
+ attr_reader :parsed_body
7
+ attr_reader :exception
8
+
9
+ def self.call(klass, response)
10
+ new(klass, response).build
11
+ end
12
+
13
+ def initialize(klass, response)
14
+ @klass = klass
15
+ @response = response
16
+ @body = response.body
17
+ begin
18
+ @parsed_body = parser.call body
19
+ rescue StandardError => e
20
+ @parsed_body = {}
21
+ @exception = e
22
+ end
23
+ end
24
+
25
+ def build
26
+ array? ? build_array : build_single
27
+ end
28
+
29
+ def build_from_klass(attributes)
30
+ klass.new(attributes).tap { |item|
31
+ item.after_build(response, exception) if item.respond_to? :after_build
32
+ }
33
+ end
34
+ private :build_from_klass
35
+
36
+ def array?
37
+ parsed_body.is_a?(Array)
38
+ end
39
+ private :array?
40
+
41
+ def build_array
42
+ parsed_body.collect { |single|
43
+ build_from_klass(single)
44
+ }
45
+ end
46
+ private :build_array
47
+
48
+ def build_single
49
+ build_from_klass parsed_body
50
+ end
51
+ private :build_single
52
+
53
+ def parser
54
+ klass.parser
55
+ end
56
+ private :parser
57
+ end
58
+ end
@@ -0,0 +1,21 @@
1
+ module Typhoid
2
+ module Multi
3
+ def remote_resources(hydra = nil)
4
+ request_queue = RequestQueue.new(self, hydra)
5
+ yield request_queue if block_given?
6
+
7
+ request_queue.run
8
+
9
+ request_queue.requests.each do |req|
10
+ parse_queued_response req
11
+ end
12
+ end
13
+
14
+ protected
15
+
16
+ def parse_queued_response(req)
17
+ varname = "@" + req.name.to_s
18
+ req.target.instance_variable_set varname.to_sym, Typhoid::Resource.build(req.klass, req.response)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,52 @@
1
+ module Typhoid
2
+ class Parser
3
+ attr_reader :json_string
4
+
5
+ def self.call(json_string)
6
+ new(json_string).parse
7
+ end
8
+
9
+ def initialize(json_string)
10
+ @json_string = json_string
11
+ end
12
+
13
+ def parse
14
+ parsed_body
15
+ end
16
+
17
+ def parsed_body
18
+ engine.call json_string
19
+ rescue
20
+ raise ReadError, json_string
21
+ end
22
+ private :parsed_body
23
+
24
+ def engine
25
+ JSON.method(:parse)
26
+ end
27
+ private :engine
28
+ end
29
+
30
+ class ReadError < StandardError
31
+ attr_reader :body
32
+ def initialize(body)
33
+ @body = body
34
+ end
35
+
36
+ def to_s
37
+ "Could not parse JSON body: #{cleaned_body}"
38
+ end
39
+
40
+ def cleaned_body
41
+ clean = body[0..10]
42
+ clean = clean + "..." if add_dots?
43
+ clean
44
+ end
45
+ private :cleaned_body
46
+
47
+ def add_dots?
48
+ body.length > 10
49
+ end
50
+ private :add_dots?
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ module Typhoid
2
+ class QueuedRequest
3
+ attr_accessor :name, :request, :target, :klass
4
+ attr_accessor :on_complete
5
+
6
+ def initialize(hydra, name, req, target)
7
+ self.name = name
8
+ self.request = Typhoeus::Request.new(req.request_uri, req.options)
9
+ self.klass = req.klass
10
+ self.target = target
11
+ hydra.queue(self.request)
12
+ end
13
+
14
+ def on_complete
15
+ self.request.on_complete do
16
+ yield self if block_given?
17
+ end
18
+ end
19
+
20
+ def status
21
+ self.request.handled_response.code
22
+ end
23
+
24
+ def response
25
+ self.request.handled_response
26
+ end
27
+
28
+ def klass
29
+ @klass
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ module Typhoid
2
+ class RequestBuilder
3
+ attr_accessor :klass
4
+ attr_writer :method
5
+
6
+ def initialize(klass, uri, options = {})
7
+ @uri = uri
8
+ @request_options = options
9
+ @klass = klass
10
+ end
11
+
12
+ def request_uri
13
+ @uri
14
+ end
15
+
16
+ def options
17
+ @request_options
18
+ end
19
+
20
+ def http_method
21
+ options[:method] || :get
22
+ end
23
+
24
+ def run
25
+ klass.run(self)
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,33 @@
1
+ require 'typhoeus'
2
+
3
+ module Typhoid
4
+ class RequestQueue
5
+ attr_reader :queue
6
+ attr_accessor :target
7
+
8
+ def initialize(target, hydra = nil)
9
+ @target = target
10
+ @hydra = hydra || Typhoeus::Hydra.new
11
+ end
12
+
13
+ def resource(name, req, &block)
14
+ @queue ||= []
15
+ @queue << QueuedRequest.new(@hydra, name, req, @target)
16
+ #@queue[name].on_complete &block if block != nil
17
+ end
18
+
19
+ def resource_with_target(name, req, target, &block)
20
+ @queue ||= []
21
+ @queue << QueuedRequest.new(@hydra, name, req, target)
22
+ #@queue[name].on_complete &block if block != nil
23
+ end
24
+
25
+ def requests
26
+ @queue ||= []
27
+ end
28
+
29
+ def run
30
+ @hydra.run
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,116 @@
1
+ require 'cgi'
2
+ require 'typhoid/uri'
3
+
4
+ module Typhoid
5
+ class Resource
6
+ include Typhoid::Multi
7
+ include Typhoid::Attributes
8
+
9
+ class << self
10
+ attr_accessor :site, :path
11
+ end
12
+
13
+ attr_accessor :resource_exception
14
+
15
+ def self.build_request(uri, options = {})
16
+ Typhoid::RequestBuilder.new(self, uri, options)
17
+ end
18
+
19
+ def self.run(request)
20
+ method = request.http_method
21
+ build(request.klass, (Typhoeus::Request.send method, request.request_uri, request.options))
22
+ end
23
+
24
+ def self.uri_join(*paths)
25
+ Uri.new(*paths).to_s
26
+ end
27
+
28
+ # Get this request URI based on site and path, can attach
29
+ # more paths
30
+ def self.request_uri(*more_paths)
31
+ uri_join site, path, *more_paths
32
+ end
33
+
34
+ def initialize(params = {})
35
+ load_values(params)
36
+ end
37
+
38
+ def success?
39
+ !resource_exception
40
+ end
41
+
42
+ def save!(method = nil)
43
+ save method
44
+ raise resource_exception unless success?
45
+ end
46
+
47
+ def destroy!
48
+ destroy
49
+ raise resource_exception unless success?
50
+ end
51
+
52
+ def save(method = nil)
53
+ request_and_load do
54
+ Typhoeus::Request.send save_http_method(method), save_request.request_uri, save_request.options
55
+ end
56
+ end
57
+
58
+ def destroy
59
+ request_and_load do
60
+ Typhoeus::Request.delete(delete_request.request_uri, delete_request.options)
61
+ end
62
+ end
63
+
64
+ def save_request
65
+ (new_record?) ? create_request : update_request
66
+ end
67
+
68
+ def save_http_method(method = nil)
69
+ return method if method
70
+ (new_record?) ? :post : :put
71
+ end
72
+
73
+ # Request URI is either in the object we retrieveed initially, built from
74
+ # site + path + id, or fail to the regular class#request_uri
75
+ #
76
+ # Also, check that the server we're speaking to isn't hypermedia inclined so
77
+ # look at our attributes for a URI
78
+ def request_uri
79
+ attributes["uri"] || (new_record? ? self.class.request_uri : self.class.request_uri(id))
80
+ end
81
+
82
+ def request_and_load(&block)
83
+ self.resource_exception = nil
84
+ response = yield
85
+ self.class.load_values(self, response)
86
+ success?
87
+ end
88
+
89
+ def persisted?
90
+ !new_record?
91
+ end
92
+
93
+ def new_record?
94
+ id.to_s.length < 1
95
+ end
96
+ alias new? new_record?
97
+
98
+ protected
99
+
100
+ def to_params
101
+ attributes
102
+ end
103
+
104
+ def create_request(method = :post)
105
+ Typhoid::RequestBuilder.new(self.class, request_uri, :body => to_params.to_json, :method => method, :headers => {"Content-Type" => 'application/json'})
106
+ end
107
+
108
+ def update_request(method = :put)
109
+ Typhoid::RequestBuilder.new(self.class, request_uri, :body => to_params.to_json, :method => method, :headers => {"Content-Type" => 'application/json'})
110
+ end
111
+
112
+ def delete_request(method = :delete)
113
+ Typhoid::RequestBuilder.new(self.class, request_uri, :method => method)
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,37 @@
1
+ require 'uri'
2
+ module Typhoid
3
+ class Uri
4
+ private
5
+ attr_writer :base
6
+ attr_writer :paths
7
+
8
+ public
9
+ attr_reader :base
10
+ attr_reader :paths
11
+
12
+ def initialize(*paths)
13
+ self.base = URI.parse paths.shift.to_s
14
+ self.paths = sanitize(base.path) + sanitize(paths)
15
+ base.path = ""
16
+ raise "Invalid Base on #uri_join: #{base}" unless base.scheme || base.host
17
+ end
18
+
19
+ def join(*more_paths)
20
+ full_path = (paths + sanitize(more_paths)).join "/"
21
+ base.clone.merge(full_path).to_s
22
+ end
23
+
24
+ def to_s
25
+ join
26
+ end
27
+
28
+ def sanitize(*need_sanitizing)
29
+ need_sanitizing.
30
+ flatten.
31
+ compact.
32
+ map { |p| p.to_s.split("/").compact.delete_if(&:empty?) }.
33
+ flatten
34
+ end
35
+ private :sanitize
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Typhoid
2
+ VERSION = "0.0.1"
3
+ end
data/lib/typhoid.rb ADDED
@@ -0,0 +1,10 @@
1
+ require "typhoid/version"
2
+ require 'typhoid/uri'
3
+ require 'typhoid/parser'
4
+ require 'typhoid/builder'
5
+ require "typhoid/request_queue"
6
+ require "typhoid/queued_request"
7
+ require "typhoid/multi"
8
+ require 'typhoid/attributes'
9
+ require 'typhoid/resource'
10
+ require 'typhoid/request_builder'
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ require 'json'
9
+
10
+ Dir["spec/support/**/*.rb"].each {|f| require "./#{f}"}
11
+
12
+ RSpec.configure do |config|
13
+ config.treat_symbols_as_metadata_keys_with_true_values = true
14
+ config.run_all_when_everything_filtered = true
15
+ config.filter_run :focus
16
+ config.color = true
17
+ end
@@ -0,0 +1,5 @@
1
+ require 'typhoid'
2
+
3
+ class Controller
4
+ include Typhoid::Multi
5
+ end
@@ -0,0 +1,15 @@
1
+ require 'typhoid'
2
+
3
+ class Game < Typhoid::Resource
4
+ field :id
5
+ field :team_1_name
6
+ field :team_2_name
7
+ field :start_time
8
+
9
+ self.site = 'http://localhost:3000/'
10
+ self.path = 'games/'
11
+
12
+ def self.get_game
13
+ build_request("http://localhost:3000/games/1")
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ require 'typhoid'
2
+
3
+ class PlayerStat < Typhoid::Resource
4
+ field :player_name
5
+ field :goals
6
+
7
+ def self.get_stats
8
+ build_request("http://localhost:3000/stats/2")
9
+ end
10
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+ module Typhoid
3
+ describe Builder do
4
+ let(:example_json) { <<-JSON
5
+ {
6
+ "metadata": {
7
+ "current_user": {
8
+ "first_name": "Jon",
9
+ "id": 1,
10
+ "last_name": "Gilmore",
11
+ "uri": "http://user-service.dev/users/1",
12
+ "user_name": "admin"
13
+ }
14
+ },
15
+ "result": {
16
+ "first_name": "Jon",
17
+ "id": 15,
18
+ "last_name": "Phenow",
19
+ "type": "orphan",
20
+ "uri": "http://user-service.dev/personas/15",
21
+ "user": null
22
+ }
23
+ }
24
+ JSON
25
+ }
26
+
27
+ let(:example_array) { <<-JSON
28
+ [{"metadata": null }, {"metadata": null }]
29
+ JSON
30
+ }
31
+ let(:klass) { Resource }
32
+ let(:response) { double body: mocked_body, success?: mocked_success }
33
+ let(:mocked_body) { example_json }
34
+ let(:mocked_success) { true }
35
+ describe "class" do
36
+ subject { Builder }
37
+ describe "call" do
38
+ subject { Builder.call klass, response }
39
+ it { should be_a Resource }
40
+ its(:attributes) { should have_key "metadata" }
41
+ its(:attributes) { should have_key "result" }
42
+ end
43
+ end
44
+
45
+ describe "instance" do
46
+ subject { Builder.new klass, response }
47
+ describe "successful" do
48
+ describe "singular" do
49
+ it "calls expected building methods" do
50
+ klass.any_instance.should_receive(:after_build).once
51
+ subject.build.should be_a Resource
52
+ end
53
+ end
54
+
55
+ describe "array" do
56
+ let(:mocked_body) { example_array }
57
+ it "calls expected building methods" do
58
+ subject.build.should be_an Array
59
+ end
60
+ end
61
+ end
62
+
63
+ describe "unsuccessful" do
64
+ let(:mocked_success) { false }
65
+ subject { Builder.new(klass, response).build }
66
+ describe "singular" do
67
+ it { should be_a Resource }
68
+ its(:resource_exception) { should_not be_nil }
69
+ its(:attributes) { should have_key "metadata" }
70
+ end
71
+
72
+ describe "array" do
73
+ let(:mocked_body) { example_array }
74
+ subject { Builder.new(klass, response).build.first }
75
+
76
+ it { should be_a Resource }
77
+ its(:resource_exception) { should_not be_nil }
78
+ its(:attributes) { should have_key "metadata" }
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,33 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'json'
3
+
4
+
5
+ describe Typhoid::Multi do
6
+ context "making multiple requests" do
7
+ before(:each) do
8
+ @fake_hydra = Typhoeus::Hydra.new
9
+ game = Typhoeus::Response.new(:code => 200, :headers => "", :body => {"team_1_name" => "Bears"}.to_json, :time => 0.03)
10
+ @fake_hydra.stub(:get, "http://localhost:3000/games/1").and_return(game)
11
+
12
+ stats = Typhoeus::Response.new(:code => 200, :headers => "",
13
+ :body => [{'player_name' => 'Bob', 'goals' => 1}, {'player_name' => 'Mike', 'goals' => 1}].to_json, :time => 0.02)
14
+ @fake_hydra.stub(:get, "http://localhost:3000/stats/2").and_return(stats)
15
+ end
16
+
17
+ it "should assign the response to instance variables" do
18
+ controller = Controller.new
19
+ controller.remote_resources(@fake_hydra) do |req|
20
+ req.resource(:game, Game.get_game)
21
+ req.resource(:stats, PlayerStat.get_stats)
22
+ end
23
+ #games returns a single object
24
+ controller.instance_variable_get("@game").class.should eql Game
25
+ controller.instance_variable_get("@game").team_1_name.should eql "Bears"
26
+
27
+ #stats returns an array
28
+ controller.instance_variable_get("@stats").class.should eql Array
29
+ controller.instance_variable_get("@stats")[0].class.should eql PlayerStat
30
+ controller.instance_variable_get("@stats")[0].player_name.should eql 'Bob'
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ module Typhoid
4
+ describe Parser do
5
+ let(:example_json) { <<-JSON
6
+ {
7
+ "metadata": {
8
+ "current_user": {
9
+ "first_name": "Jon",
10
+ "id": 1,
11
+ "last_name": "Gilmore",
12
+ "uri": "http://user-service.dev/users/1",
13
+ "user_name": "admin"
14
+ }
15
+ },
16
+ "result": {
17
+ "first_name": "Jon",
18
+ "id": 15,
19
+ "last_name": "Phenow",
20
+ "type": "orphan",
21
+ "uri": "http://user-service.dev/personas/15",
22
+ "user": null
23
+ }
24
+ }
25
+ JSON
26
+ }
27
+
28
+ let(:example_array) { <<-JSON
29
+ [{"metadata": null }, {"metadata": null }]
30
+ JSON
31
+ }
32
+
33
+ describe "class" do
34
+ subject { Parser }
35
+
36
+ describe "call" do
37
+ subject { Parser.call(example_json) }
38
+
39
+ it { should be_a Hash }
40
+ it "has an expected element" do
41
+ subject["result"]["first_name"].should == "Jon"
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "instance" do
47
+ subject { Parser.new example_json }
48
+ describe "parse" do
49
+ it "looks like a hash" do
50
+ subject.parse.should be_a Hash
51
+ end
52
+
53
+ it "has an expected element" do
54
+ subject.parse["result"]["type"].should == "orphan"
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Typhoid::RequestBuilder do
4
+ context "a request builder object" do
5
+ it "should provide an http method by default" do
6
+ req = Typhoid::RequestBuilder.new(Game, 'http://localhost/')
7
+ req.http_method.should eql :get
8
+ end
9
+
10
+ it "should set http method from options" do
11
+ req = Typhoid::RequestBuilder.new(Game, 'http://localhost', :method => :post)
12
+ req.http_method.should eql :post
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,143 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Typhoid::Resource do
4
+ it "synchronizes field with attribute" do
5
+ response_data = {"team_1_name" => 'Bears', "team_2_name" => 'Lions'}
6
+ game = Game.new(response_data)
7
+ game.team_1_name.should == 'Bears'
8
+ game.attributes["team_1_name"].should == 'Bears'
9
+ game.attributes["team_1_name"] = 'Da Bears'
10
+ game.attributes["team_1_name"].should == 'Da Bears'
11
+ game.team_1_name.should == 'Da Bears'
12
+
13
+ game.team_1_name = 'Orange'
14
+ game.team_1_name.should == 'Orange'
15
+ game.attributes["team_1_name"].should == 'Orange'
16
+ end
17
+
18
+ it "should have fields defined" do
19
+ game = Game.new
20
+ game.should respond_to(:team_1_name)
21
+ end
22
+
23
+ it "should populate defined attributes" do
24
+ response_data = {"team_1_name" => 'Bears', "team_2_name" => 'Lions'}
25
+ game = Game.new(response_data)
26
+ game.team_1_name.should eql 'Bears'
27
+ game.start_time.should be_nil
28
+ end
29
+
30
+ it "should populate attributes" do
31
+ game = Game.new({"team_1_name" => 'Bears', "team_2_name" => 'Lions'})
32
+ game.read_attribute(:team_1_name).should eql 'Bears'
33
+ game[:team_2_name].should eql 'Lions'
34
+ end
35
+
36
+ it "should return the request path" do
37
+ game = Game.new
38
+ game.request_uri.should eql "http://localhost:3000/games"
39
+ end
40
+
41
+ context "making a standalone request" do
42
+ after { hydra.clear_stubs }
43
+ let(:hydra) { Typhoeus::Hydra.hydra }
44
+ let(:game_response) { Typhoeus::Response.new(:code => 200, :headers => "", :body => {"team_1_name" => "Bears", "id" => "1"}.to_json) }
45
+ let(:failed_game_response) { Typhoeus::Response.new(:code => 404, :headers => "", :body => {}.to_json) }
46
+ it "should retrieve an object" do
47
+ hydra.stub(:get, "http://localhost:3000/games/1").and_return(game_response)
48
+
49
+ game = Game.get_game.run
50
+ game.class.should eql Game
51
+ game.team_1_name.should eql 'Bears'
52
+ end
53
+
54
+ it "raises error on save!" do
55
+ hydra.stub(:post, "http://localhost:3000/games").and_return(failed_game_response)
56
+
57
+ game = Game.new
58
+ expect { game.save! }.to raise_error
59
+ end
60
+
61
+ it "raises error on destroy!" do
62
+ hydra.stub(:delete, "http://localhost:3000/games/1").and_return(failed_game_response)
63
+
64
+ game = Game.new("id" => 1, "team_1_name" => 'Tigers')
65
+ expect { game.destroy! }.to raise_error
66
+ end
67
+
68
+ it "raises error on save!" do
69
+ hydra.stub(:post, "http://localhost:3000/games").and_return(game_response)
70
+
71
+ game = Game.new
72
+ expect { game.save! }.to_not raise_error
73
+ end
74
+
75
+ it "raises error on save!" do
76
+ hydra.stub(:delete, "http://localhost:3000/games/1").and_return(game_response)
77
+
78
+ game = Game.new("id" => 1, "team_1_name" => 'Tigers')
79
+ expect { game.destroy! }.to_not raise_error
80
+ end
81
+
82
+ it "should create an object" do
83
+ hydra.stub(:post, "http://localhost:3000/games").and_return(game_response)
84
+
85
+ game = Game.new
86
+ game.save
87
+
88
+ game.id.should == "1"
89
+ game.team_1_name.should == "Bears"
90
+ end
91
+
92
+ it "should update an object" do
93
+ update_response = Typhoeus::Response.new(:code => 200, :headers => "", :body => {"team_1_name" => "Bears", "id" => "1"}.to_json)
94
+ hydra.stub(:put, "http://localhost:3000/games/1").and_return(update_response)
95
+
96
+ game = Game.new("id" => 1, "team_1_name" => 'Tigers')
97
+ game.save
98
+
99
+ game.resource_exception.should be_nil
100
+ game.team_1_name.should == "Bears"
101
+ end
102
+
103
+ it "should delete an object" do
104
+ hydra.stub(:delete, "http://localhost:3000/games/1").and_return(game_response)
105
+
106
+ game = Game.new("id" => 1, "team_1_name" => 'Tigers')
107
+ game.destroy
108
+
109
+ game.resource_exception.should be nil
110
+
111
+ end
112
+
113
+ it "should be able to specify save http verb" do
114
+ update_response = Typhoeus::Response.new(:code => 200, :headers => "", :body => {"team_1_name" => "Bears", "id" => "1"}.to_json)
115
+ hydra.stub(:post, "http://localhost:3000/games/1").and_return(update_response)
116
+
117
+ game = Game.new("id" => 1, "team_1_name" => 'Tigers')
118
+ game.save(:post)
119
+
120
+ game.resource_exception.should be nil
121
+
122
+ end
123
+ end
124
+
125
+ context "handling bad requests" do
126
+ let(:fake_hydra) { Typhoeus::Hydra.new }
127
+ before do
128
+ bad_game = Typhoeus::Response.new(:code => 500, :headers => "", :body => "<htmlasdfasdfasdf")
129
+ fake_hydra.stub(:get, "http://localhost:3000/games/1").and_return(bad_game)
130
+ end
131
+
132
+ it "should assign an exception object on a bad request" do
133
+ controller = Controller.new
134
+ controller.remote_resources(fake_hydra) do |req|
135
+ req.resource(:game, Game.get_game)
136
+ end
137
+
138
+ bad_game = controller.instance_variable_get("@game")
139
+ bad_game.team_1_name.should be_nil
140
+ bad_game.resource_exception.class.should be_true
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Typhoid do
4
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ module Typhoid
3
+ describe "Uri" do
4
+ subject { Uri.new *uris }
5
+ let(:uris) { ["http://localhost/", "users"] }
6
+
7
+ its(:to_s) { should == "http://localhost/users" }
8
+ it "sets base" do
9
+ subject.base.to_s.should == "http://localhost"
10
+ end
11
+
12
+ it "sets path" do
13
+ subject.paths.should == ["users"]
14
+ end
15
+
16
+ it "appends paths" do
17
+ subject.join("/","/a/","b","/c","d/").should == "http://localhost/users/a/b/c/d"
18
+ end
19
+
20
+ it "when joining it doesn't change itself" do
21
+ expect {
22
+ subject.join("/","/a/","b","/c","d/").should == "http://localhost/users/a/b/c/d"
23
+ }.
24
+ to_not change { subject.to_s }
25
+ end
26
+ end
27
+ end
data/typhoid.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/typhoid/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Doug Rohde"]
6
+ gem.email = ["doug.rohde@tstmedia.com"]
7
+ gem.description = %q{A lightweight ORM-like wrapper around Typhoeus}
8
+ gem.summary = %q{A lightweight ORM-like wrapper around Typhoeus}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "typhoid"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Typhoid::VERSION
17
+
18
+ gem.add_dependency 'typhoeus'
19
+
20
+ gem.add_development_dependency 'rspec'
21
+ gem.add_development_dependency 'json_pure', [">= 1.4.1"]
22
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: typhoid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Doug Rohde
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: typhoeus
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: json_pure
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.4.1
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.4.1
62
+ description: A lightweight ORM-like wrapper around Typhoeus
63
+ email:
64
+ - doug.rohde@tstmedia.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - .rvmrc
71
+ - Gemfile
72
+ - Gemfile.lock
73
+ - LICENSE
74
+ - README.md
75
+ - Rakefile
76
+ - lib/typhoid.rb
77
+ - lib/typhoid/attributes.rb
78
+ - lib/typhoid/builder.rb
79
+ - lib/typhoid/multi.rb
80
+ - lib/typhoid/parser.rb
81
+ - lib/typhoid/queued_request.rb
82
+ - lib/typhoid/request_builder.rb
83
+ - lib/typhoid/request_queue.rb
84
+ - lib/typhoid/resource.rb
85
+ - lib/typhoid/uri.rb
86
+ - lib/typhoid/version.rb
87
+ - spec/spec_helper.rb
88
+ - spec/support/controller.rb
89
+ - spec/support/game.rb
90
+ - spec/support/player_stat.rb
91
+ - spec/typhoid/builder_spec.rb
92
+ - spec/typhoid/multi_spec.rb
93
+ - spec/typhoid/parser_spec.rb
94
+ - spec/typhoid/request_builder_spec.rb
95
+ - spec/typhoid/resource_spec.rb
96
+ - spec/typhoid/typhoid_spec.rb
97
+ - spec/typhoid/uri_spec.rb
98
+ - typhoid.gemspec
99
+ homepage: ''
100
+ licenses: []
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ segments:
112
+ - 0
113
+ hash: -3289300841199421232
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ segments:
121
+ - 0
122
+ hash: -3289300841199421232
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 1.8.24
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: A lightweight ORM-like wrapper around Typhoeus
129
+ test_files:
130
+ - spec/spec_helper.rb
131
+ - spec/support/controller.rb
132
+ - spec/support/game.rb
133
+ - spec/support/player_stat.rb
134
+ - spec/typhoid/builder_spec.rb
135
+ - spec/typhoid/multi_spec.rb
136
+ - spec/typhoid/parser_spec.rb
137
+ - spec/typhoid/request_builder_spec.rb
138
+ - spec/typhoid/resource_spec.rb
139
+ - spec/typhoid/typhoid_spec.rb
140
+ - spec/typhoid/uri_spec.rb