plaza 0.0.1 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 68d3fd27e2537f601b602d6e07c27e3ef5b5d117
4
- data.tar.gz: 49f54958a62cd48970544d94ac2931654c8d7ee0
3
+ metadata.gz: b733efabb58c0d5ed5220b881edeb953fb1df5e6
4
+ data.tar.gz: a2868546c5f666fbf160134481584049efd8a813
5
5
  SHA512:
6
- metadata.gz: 0a6c1724db2dd71a2daa0c456adf85cc868f4c73e632beea41183a2091d739af6626459dcde43f5633b5da463bbf48e3a2ea381e63311909dd1dfc84b48c2d76
7
- data.tar.gz: 9910d078ea141799434f8df91e3b692864f3179d037e75f435589eb7b6f72882fb185fe46d79d1ae7a2e216506daab424f9464c0004d29cd7a534d0c528e4ebe
6
+ metadata.gz: 1f02c45778f2763dce6d67fa1bd9122e957cb9de19f0bbd50b1c7b0fa969b2f2bb3c1c894933be6cf9e7e76b3bb80f035143cebd6d73d0842477bccabe17d7be
7
+ data.tar.gz: 589532ff59a223dbfd0a32d29be2c8badfce4740b2f5e129c45aebfc52166ffbd570ca1c1e9247b3d7b14dbc09820b2d507fbb780e56e96bcb211924740194fc
data/Gemfile CHANGED
@@ -2,3 +2,17 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in plaza.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'webmock', '~> 1.18' # HTTP stub/expectations
8
+ gem 'simplecov', '~> 0.7.1' # Code Coverage
9
+ end
10
+
11
+ # Development gems that aren't run-time dependencies
12
+ group :development do
13
+ end
14
+
15
+ group :development, :test do
16
+ gem 'guard-rspec'
17
+ gem 'pry-nav', '~> 0.2.3' # Pry control flow
18
+ end
data/Rakefile CHANGED
@@ -1,2 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
@@ -0,0 +1,41 @@
1
+ module Plaza
2
+ module BaseAdapter
3
+ def handle_response(response)
4
+ begin
5
+ if response.success?
6
+ return response_to_hash(response)
7
+ else
8
+ handle_error(response)
9
+ end
10
+ rescue JSON::ParserError=> jsonError
11
+ error = Plaza::Error.new(jsonError, jsonError.message)
12
+ raise(error,error.to_s )
13
+ end
14
+ end
15
+
16
+ def handle_error(response)
17
+ response_hash = response_to_hash(response)
18
+ error_hash = response_hash.has_key?(:error) ? response_hash[:error] : response_hash["error"]
19
+ unless error_hash
20
+ error_hash = response_hash.has_key?(:errors) ? response_hash[:errors] : response_hash["errors"]
21
+ end
22
+ #test for both singular error and plural errors
23
+ logger.warn("Response returned an error code #{response.status} - #{response_hash}")
24
+ case response.status.to_i
25
+ when 422
26
+ error = Plaza::ResourceInvalid.new(response, "#{error_hash}", error_hash)
27
+ else
28
+ error = Plaza::Error.new(response, "#{error_hash}")
29
+ end
30
+ raise(error,error.to_s )
31
+ end
32
+
33
+ def response_to_hash(response)
34
+ if response.body.kind_of? Hash
35
+ response.body
36
+ else
37
+ JSON.parse(response)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,57 @@
1
+ class Plaza::RestfulAdapter
2
+ include Plaza::BaseAdapter
3
+ attr_reader :request, :logger
4
+
5
+ def initialize(resource)
6
+ @singular_name = resource.singular_name
7
+ @plural_name = resource.plural_name
8
+ @request = Plaza::Request.new(resource.plaza_config)
9
+ @logger = Plaza.configuration(resource.plaza_config).logger
10
+ end
11
+
12
+ def index(query_params = nil)
13
+ handle_response(request.get(base_url(query_params)))
14
+ end
15
+
16
+ def show(id)
17
+ hash = handle_response(request.get(resource_url(id)))
18
+ hash.fetch(@singular_name){hash}
19
+ end
20
+
21
+ def update(id, data)
22
+ hash = handle_response(request.put(resource_url(id), data))
23
+ hash.fetch(@singular_name){hash}
24
+ end
25
+
26
+ def create(data)
27
+ hash = handle_response(request.post(base_url, data))
28
+ hash.fetch(@singular_name){hash}
29
+ end
30
+
31
+ def delete(id)
32
+ hash = handle_response(request.delete(resource_url(id)))
33
+ hash.fetch(@singular_name){hash}
34
+ end
35
+
36
+ def has_many(id, relation)
37
+ hash = handle_response(request.get(has_many_url(id,relation)))
38
+ hash.fetch(@singular_name){hash}
39
+ end
40
+
41
+ private
42
+ def base_url(query_params = nil)
43
+ url = "#{@plural_name}.json"
44
+ url << "?#{URI.encode_www_form(query_params)}" if query_params
45
+ url
46
+ end
47
+
48
+ def resource_url(id)
49
+ "#{base_url.chomp('.json')}/#{id}.json"
50
+ end
51
+
52
+ def has_many_url(id, relation)
53
+ "#{resource_url(id).chomp('.json')}/#{relation}.json"
54
+ end
55
+
56
+ end
57
+
@@ -0,0 +1,5 @@
1
+ require_relative 'request'
2
+ require_relative 'response'
3
+
4
+ require_relative 'adapters/base_adapter'
5
+ require_relative 'adapters/restful_adapter'
@@ -0,0 +1,33 @@
1
+ require 'logger'
2
+
3
+ module Plaza
4
+ class Configuration
5
+
6
+ #things that do have defaults only get writers
7
+ attr_writer :use_cache, :cache_entity_store, :cache_meta_store
8
+
9
+ def base_url(url = nil)
10
+ url ? @url = url : @url
11
+ end
12
+ alias_method :base_url=, :base_url
13
+
14
+ def logger(logger = nil)
15
+ @logger ||= Logger.new(STDOUT)
16
+ logger ? @logger = logger : @logger
17
+ end
18
+ alias_method :logger=, :logger
19
+
20
+ def use_cache?
21
+ @use_cache ||= false
22
+ end
23
+
24
+ def cache_meta_store
25
+ @cache_meta_store ||= 'file:/tmp/cache/meta'
26
+ end
27
+
28
+ def cache_entity_store
29
+ @cache_meta_store ||= 'file:/tmp/cache/body'
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ module Plaza
2
+ module Inflector
3
+ extend self
4
+
5
+ def singularize(str)
6
+ str.strip!
7
+ str.gsub!(/ies$/,'y')
8
+ str.chomp('s')
9
+ end
10
+
11
+ def classify(table_name)
12
+ singularize(table_name.split('_').map(&:capitalize).join)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require 'plaza/models/error'
2
+
3
+ module Plaza::Middleware
4
+ class Exceptions < Faraday::Middleware
5
+ def call(env)
6
+ begin
7
+ @app.call(env)
8
+ rescue Faraday::Error::ConnectionFailed => e
9
+ error = Plaza::ConnectionError.new(nil, 'Service is not available.')
10
+ raise(error, error.to_s)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ require 'faraday'
2
+
3
+ module Plaza
4
+ module Middleware
5
+ class UserId < Faraday::Middleware
6
+ def call(env)
7
+ env.request_headers[:x_user_id] = Thread.current[:x_user_id].to_s
8
+ @app.call(env)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,40 @@
1
+ module Plaza
2
+ module BaseModel
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ ## assumes a shallow hierarchy where there is only one
9
+ ## root element that contains an array of hashes
10
+ ## For Example:
11
+ ## {:campaign_ranks=>[{:key=>1, :rank=>"0.4"}, {:key=>2, :rank=>"0.9"}]}
12
+ def collection(response)
13
+ response.values.first.collect do |obj|
14
+ self.new(obj)
15
+ end
16
+ end
17
+
18
+ def adapter
19
+ Plaza.adapter(self)
20
+ end
21
+
22
+ end
23
+
24
+ def adapter
25
+ self.class.adapter
26
+ end
27
+
28
+ def singular_name
29
+ self.class.to_s.split('::').last.scan(/[A-Z][a-z]+/).join('_').downcase
30
+ end
31
+
32
+ def serialize
33
+ {singular_name => attributes}
34
+ end
35
+
36
+ def to_json
37
+ self.serialize.to_json
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ module Plaza
2
+ class Error < ::StandardError
3
+ attr_reader :response
4
+
5
+ def initialize(response, message = nil)
6
+ @response = response
7
+ @message = message || "Failed."
8
+ @message << " Response code = #{status}." if status
9
+ end
10
+
11
+ def status
12
+ response.respond_to?(:status) ? response.status : nil
13
+ end
14
+
15
+ def to_s
16
+ @message
17
+ end
18
+ alias :to_str :to_s
19
+ end
20
+
21
+
22
+ class ConnectionError < Error; end
23
+
24
+ #422
25
+ class ResourceInvalid < Error
26
+ attr_reader :errors
27
+ def initialize(response, message = nil, error_hash={})
28
+ super(response, message)
29
+ @errors = error_hash
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,139 @@
1
+ module Plaza
2
+ module RestfulModel
3
+ ErrorResponse = Struct.new(:code, :body) do
4
+ def to_str
5
+ body
6
+ end
7
+ end
8
+
9
+ def self.included(base)
10
+ base.class_eval do
11
+ include Virtus.model
12
+ include Plaza::BaseModel
13
+ attribute :id, Integer
14
+ attribute :errors, Hash
15
+
16
+ def serialize
17
+ attrs = attributes.delete_if{|k, v| k.to_sym == :id && v.nil?}
18
+ attrs = attrs.delete_if{|k, v| [:updated_at, :created_at].include?(k.to_sym)}
19
+ attrs.delete(:errors)
20
+ {singular_name => attrs}
21
+ end
22
+ end
23
+
24
+ base.extend ClassMethods
25
+ end
26
+
27
+ module ClassMethods
28
+
29
+ def all
30
+ collection(adapter.index)
31
+ end
32
+
33
+ def find(id)
34
+ self.new( adapter.show(id) )
35
+ end
36
+
37
+ def adapter
38
+ Plaza::RestfulAdapter.new(self)
39
+ end
40
+
41
+ def create(attributes)
42
+ resource = self.new(attributes)
43
+ resource.save && resource
44
+ end
45
+
46
+ def where(attributes)
47
+ collection( adapter.index(attributes) )
48
+ end
49
+
50
+ def plural_name
51
+ singular_name + 's'
52
+ end
53
+
54
+ def singular_name
55
+ self.to_s.split('::').last.scan(/[A-Z][a-z]+/).join('_').downcase
56
+ end
57
+
58
+ # Class Configuration
59
+ #
60
+ def has_many(*relations)
61
+ relations.each do |r|
62
+ define_method(r) do
63
+ class_name = Plaza::Inflector.classify(r.to_s)
64
+ Plaza.const_get(class_name).collection(adapter.has_many(self.id,r))
65
+ end
66
+ end
67
+ end
68
+
69
+ def plaza_config(config = nil)
70
+ @plaza_config ||= :default
71
+ config ? @plaza_config = config : @plaza_config
72
+ end
73
+
74
+ alias_method :plaza_config=, :plaza_config
75
+ end
76
+
77
+ def plaza_config
78
+ self.class.plaza_config
79
+ end
80
+
81
+ def delete
82
+ self.class.adapter.delete(self.id)
83
+ end
84
+
85
+ def error_messages
86
+ if errors.empty?
87
+ []
88
+ else
89
+ errors.collect{|k, v| v.collect{|val| "#{k} #{val}"}}.flatten
90
+ end
91
+ end
92
+
93
+ def new_record?
94
+ !persisted?
95
+ end
96
+
97
+ def persisted?
98
+ self.id.present?
99
+ end
100
+
101
+ def save
102
+ begin
103
+ if self.id
104
+ self.attributes = self.class.adapter.update(self.id, self.serialize)
105
+ else
106
+ self.attributes = self.class.adapter.create(self.serialize)
107
+ end
108
+ rescue Plaza::ResourceInvalid => e
109
+ self.errors.merge!(e.errors)
110
+ end
111
+ self.errors.empty?
112
+ end
113
+
114
+ def symbolize_keys(hash)
115
+ hash.inject({}){|sym_hash,(k,v)| sym_hash[k.to_sym] = v; sym_hash}
116
+ end
117
+
118
+ def to_param
119
+ self.id
120
+ end
121
+
122
+ def update_attributes(attributes_hash)
123
+ self.attributes = self.attributes.merge(self.symbolize_keys(attributes_hash))
124
+ self.save
125
+ end
126
+
127
+ def method_missing(method_name, *args, &block)
128
+ method_name = method_name.to_s
129
+ if self.respond_to?(method_name + '_id')
130
+ obj_id = self.send(method_name + '_id')
131
+ class_name = Plaza::Inflector.classify(method_name)
132
+ return Plaza.const_get(class_name).find(obj_id)
133
+ else
134
+ raise NoMethodError.new "undefined method '#{method_name}' for #{self.class}"
135
+ end
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,13 @@
1
+ require 'plaza/models/base_model'
2
+ require 'plaza/models/restful_model'
3
+ require 'plaza/models/error'
4
+
5
+ module Plaza
6
+ Dir.glob(File.join(File.dirname(__FILE__), "models", "*.rb")).each do |file_name|
7
+ base_name = File.basename(file_name).gsub("\.rb", "")
8
+ const_name = base_name.split('_').collect!{ |w| w.capitalize }.join
9
+ autoload const_name.to_sym, File.join(File.dirname(__FILE__), "models", base_name)
10
+ end
11
+ autoload "ConnectionError", File.join(File.dirname(__FILE__), "models", "error.rb")
12
+ autoload "ResourceInvalid", File.join(File.dirname(__FILE__), "models", "error.rb")
13
+ end
@@ -0,0 +1,46 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require_relative 'middleware/user_id'
4
+ require_relative 'middleware/exceptions'
5
+
6
+ module Plaza
7
+ class Request
8
+ attr_accessor :client, :connection
9
+ attr_reader :logger
10
+
11
+ def initialize(config_sym= :default)
12
+ config = Plaza.configuration(config_sym)
13
+ @connection = Faraday.new(config.base_url) do |conn|
14
+ conn.request :json
15
+ conn.response :json, :content_type => /\bjson$/
16
+
17
+ conn.use Plaza::Middleware::Exceptions
18
+ conn.use Plaza::Middleware::UserId
19
+
20
+ conn.headers[:accept] = 'application/json'
21
+ yield(conn) if block_given?
22
+
23
+ conn.adapter Faraday.default_adapter
24
+ end
25
+ @logger = config.logger
26
+ end
27
+
28
+
29
+ def get(*args)
30
+ Response.new(connection.get *args)
31
+ end
32
+
33
+ def post(*args)
34
+ Response.new(connection.post *args)
35
+ end
36
+
37
+ def put(*args)
38
+ Response.new(connection.put *args)
39
+ end
40
+
41
+ def delete(*args)
42
+ Response.new(connection.delete *args)
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,30 @@
1
+ require 'delegate'
2
+
3
+ module Plaza
4
+ class Response < SimpleDelegator
5
+ def success?
6
+ status.to_s[0].to_i == 2
7
+ end
8
+
9
+ def redirected?
10
+ status.to_s[0].to_i == 3
11
+ end
12
+
13
+ def failed?
14
+ [4, 5].include?(status.to_s[0].to_i)
15
+ end
16
+
17
+ def to_str
18
+ body.to_s
19
+ end
20
+
21
+ def code
22
+ status
23
+ end
24
+
25
+ private
26
+ def object
27
+ __getobj__
28
+ end
29
+ end
30
+ end
data/lib/plaza/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Plaza
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/plaza.rb CHANGED
@@ -1,5 +1,39 @@
1
+ require 'virtus'
2
+ require "rack/cache"
3
+
4
+ require 'plaza/configuration'
1
5
  require "plaza/version"
6
+ require 'plaza/models'
7
+ require 'plaza/request'
8
+ require 'plaza/response'
9
+ require 'plaza/adapters'
10
+ require 'plaza/inflector'
11
+ require 'restclient/components'
2
12
 
3
13
  module Plaza
4
- # Your code goes here...
14
+
15
+ class << self
16
+ def enable_cache
17
+ #this makes it so that we adhere to http cache headers when issuing
18
+ #requests
19
+ require 'rack/cache'
20
+ RestClient.enable Rack::Cache,
21
+ :metastore => self.configuration.meta_store,
22
+ :entitystore => self.configuration.entity_store
23
+ end
24
+ end
25
+
26
+ def self.configuration(component_name = :default)
27
+ @configurations ||= {}
28
+ @configurations[component_name] ||= Plaza::Configuration.new
29
+ end
30
+
31
+ def self.configure(component_name = :default, &block)
32
+ self.configuration(component_name).instance_eval(&block) if block_given?
33
+ end
34
+
35
+ def self.adapter(class_name)
36
+ Plaza.const_get("#{class_name}Adapter").new
37
+ end
38
+
5
39
  end
data/plaza.gemspec CHANGED
@@ -6,8 +6,8 @@ require 'plaza/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "plaza"
8
8
  spec.version = Plaza::VERSION
9
- spec.authors = ["Benjamin Guest"]
10
- spec.email = ["benguest@gmail.com"]
9
+ spec.authors = ["VMC Engineering"]
10
+ spec.email = ["eng@visiblemeasures.com", "benguest@gmail.com"]
11
11
  spec.summary = %q{Client for rest_area gem}
12
12
  spec.description = %q{Rest client for that works in conjuntion with rest_area gem}
13
13
  spec.homepage = ""
@@ -20,4 +20,13 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.7"
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency 'rspec', '~> 2.99'
24
+ spec.add_development_dependency 'mocha', '~> 1.1'
25
+
26
+ spec.add_runtime_dependency "rest-client", "~> 1.6"
27
+ spec.add_runtime_dependency "rest-client-components"
28
+ spec.add_runtime_dependency 'faraday', '~> 0.9.0'
29
+ spec.add_runtime_dependency 'faraday_middleware', '~>0.9.1'
30
+ spec.add_runtime_dependency "rack-cache", '~> 1.2'
31
+ spec.add_runtime_dependency "virtus", '~> 1.0'
23
32
  end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ include Plaza
3
+
4
+ describe RestfulAdapter do
5
+ context "when resource not found" do
6
+ before do
7
+ stub_request(:get, "#{Plaza.configuration.base_url}/tests/1000.json").to_return(:status=>404, :body=>{"status"=>"404","error"=>"Couldn't find tests with id=10000"}.to_json)
8
+ end
9
+
10
+ let(:mock_resource){
11
+ resource = mock("resource", :plural_name=>"tests", :singular_name=>"test")
12
+ resource.stubs(:plaza_config).returns :default
13
+ resource
14
+ }
15
+ let(:adapter){RestfulAdapter.new(mock_resource)}
16
+
17
+ it "raises generic exception" do
18
+ expect{adapter.show(1000)}.to raise_error(Plaza::Error)
19
+ end
20
+
21
+ it "raises generic exception which exposes status" do
22
+ expect{adapter.show(1000)}.to raise_error{ |error|
23
+ error.should be_a(Plaza::Error)
24
+ error.status.should == 404
25
+ error.message.should =~ /Couldn't find/
26
+ }
27
+ end
28
+
29
+ end
30
+
31
+
32
+ context "when 422 returned on create" do
33
+ before do
34
+ stub_request(:post, "#{Plaza.configuration.base_url}/tests.json").
35
+ with(:body => mock_data.to_json).
36
+ to_return(:status=>422, :body=>{:errors=>{"name"=>["has already been taken"]}}.to_json)
37
+ end
38
+
39
+ let(:mock_data){{"test"=>{"name"=> "test", "klass"=> "test"}}}
40
+
41
+ let(:mock_resource){
42
+ resource = mock("resource", :plural_name=>"tests", :singular_name=>"test")
43
+ resource.stubs(:plaza_config).returns :default
44
+ resource
45
+ }
46
+ let(:adapter){RestfulAdapter.new(mock_resource)}
47
+
48
+ it "raises ResourceInvalid exception" do
49
+ expect{adapter.create(mock_data)}.to raise_error(Plaza::ResourceInvalid)
50
+ end
51
+
52
+ it "raises ResourceInvalid exception which exposes status" do
53
+ expect{adapter.create(mock_data)}.to raise_error{ |error|
54
+ error.should be_a(Plaza::ResourceInvalid)
55
+ error.status.should == 422
56
+ error.message.should =~ /has already been taken/
57
+ }
58
+ end
59
+
60
+ it "exception exposes errors hash" do
61
+ expect{adapter.create(mock_data)}.to raise_error{ |error|
62
+ error.errors.should be_a(Hash)
63
+ error.errors.should eql({"name"=>["has already been taken"]})
64
+ }
65
+ end
66
+
67
+ end
68
+
69
+ context "when non-json returned" do
70
+ before do
71
+ stub_request(:get, "#{Plaza.configuration.base_url}/tests.json").to_return(:status=>200, :body=>"<html></html>")
72
+ end
73
+
74
+ let(:mock_resource){
75
+ resource = mock("resource", :plural_name=>"tests", :singular_name=>"test")
76
+ resource.stubs(:plaza_config).returns :default
77
+ resource
78
+ }
79
+ let(:adapter){RestfulAdapter.new(mock_resource)}
80
+
81
+ it "raises generic exception" do
82
+ expect{adapter.index}.to raise_error(Plaza::Error)
83
+ end
84
+
85
+ it "raises generic exception which exposes status" do
86
+ expect{adapter.index}.to raise_error{ |error|
87
+ error.should be_a(Plaza::Error)
88
+ error.status.should == nil
89
+ error.message.should =~ /unexpected token/
90
+ }
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ include Plaza
3
+
4
+ describe Plaza::Inflector do
5
+
6
+ describe '#singularize' do
7
+ it 'should return singularized random word with s at the end' do
8
+ Inflector.singularize('foobars').should == 'foobar'
9
+ Inflector.singularize('intentss').should == 'intents'
10
+ end
11
+
12
+ it 'should change ies to y' do
13
+ Inflector.singularize('entries').should == 'entry'
14
+ end
15
+
16
+ it 'should not change ies in middle of word' do
17
+ Inflector.singularize('fiesties ').should == 'fiesty'
18
+ end
19
+ end
20
+
21
+ describe '#classify' do
22
+ it 'should classify strings with and s at the end' do
23
+ Inflector.classify('foobars ').should == 'Foobar'
24
+ end
25
+
26
+ it 'should classify strings that end in ies' do
27
+ Inflector.classify('entries').should == 'Entry'
28
+ end
29
+
30
+ it 'should work with underscores' do
31
+ Inflector.classify('targeting_entries').should == 'TargetingEntry'
32
+ Inflector.classify('events_collections').should == 'EventsCollection'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,248 @@
1
+ require 'spec_helper'
2
+
3
+ class Thing
4
+ include Plaza::RestfulModel
5
+
6
+ attribute :id, Integer
7
+ attribute :name, String
8
+ attribute :amajig_id, Integer
9
+ end
10
+
11
+ class Amajig
12
+ include Plaza::RestfulModel
13
+
14
+ has_many :things, :amabobs
15
+ attribute :name
16
+ end
17
+
18
+ Plaza.configure :amabob do
19
+ base_url 'http://www.example.com/rest'
20
+ logger NullLogger.new # Specs should STFU
21
+ end
22
+
23
+ class Amabob
24
+ include Plaza::RestfulModel
25
+ plaza_config :amabob
26
+
27
+ attribute :amajig_id, Integer
28
+ end
29
+
30
+ describe Thing do
31
+
32
+ let(:things_hash){
33
+ {
34
+ 'things'=>[
35
+ {
36
+ 'id' => 1,
37
+ 'name' => 'Thing 1'
38
+ },
39
+ {
40
+ 'id' => 2,
41
+ 'name' => 'Thing 2'
42
+ }
43
+ ]
44
+ }
45
+ }
46
+
47
+ let(:thing_hash){
48
+ {
49
+ 'thing' => {
50
+ 'id' => 1,
51
+ 'name' => 'Thing One',
52
+ 'amajig_id' => 2
53
+ }
54
+ }
55
+ }
56
+
57
+ let(:amajig_hash){
58
+ {
59
+ 'amajig' => {
60
+ 'id' => 2,
61
+ 'name'=>'Jigger'
62
+ }
63
+ }
64
+ }
65
+
66
+ let(:amabobs_hash){
67
+ {
68
+ 'amabobs' =>[
69
+ {
70
+ 'id' => 3,
71
+ 'amajig_id' => 2
72
+ },
73
+ {
74
+ 'id' => 4,
75
+ 'amajig_id' => 2
76
+ }
77
+ ]
78
+ }
79
+ }
80
+
81
+ describe 'attributes' do
82
+ it{ should respond_to :errors }
83
+
84
+ it 'should raise NoMethodError for unknown method' do
85
+ expect{ Thing.new(id:10).foobar }.to raise_error NoMethodError
86
+ end
87
+ end
88
+
89
+ describe '.all' do
90
+ before do
91
+ stub_request(:get, 'http://example.com/rest/things.json').to_return(body: things_hash.to_json)
92
+ end
93
+
94
+ it 'should return the correct number of things' do
95
+ expect(Thing.all.count).to eq 2
96
+ end
97
+
98
+ it 'should return things of class Thing' do
99
+ expect(Thing.all.first.class).to eq Thing
100
+ end
101
+
102
+ it 'should return things with the correct attribures' do
103
+ expect(Thing.all.first.name).to eq 'Thing 1'
104
+ end
105
+ end
106
+
107
+ describe '#plaza_config / plaza_config' do
108
+ it 'should be able to set in class definition' do
109
+ expect(Thing.new.plaza_config).to eq :default
110
+ end
111
+ it 'should be able to set plaza_config' do
112
+ Thing.plaza_config = :custom
113
+ expect(Thing.new.plaza_config).to eq :custom
114
+ Thing.plaza_config = :default
115
+ end
116
+ end
117
+
118
+ describe '.create(attributes)' do
119
+ before do
120
+ hash = {'thing'=> {'id'=>nil,'name'=>'Thing One', 'amajig_id' => 2}}
121
+ stub_request(:post, 'http://example.com/rest/things.json').with(body:hash.to_json).to_return(body: thing_hash.to_json)
122
+ end
123
+
124
+ it 'should post new object and return thing object' do
125
+ stub_request(:post, "http://example.com/rest/things.json").
126
+ with(:body => "{\"thing\":{\"name\":\"Thing One\",\"amajig_id\":2}}",
127
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'}).
128
+ to_return(:status => 200, :body => thing_hash.to_json, :headers => {})
129
+ thing = Thing.create(:name => 'Thing One', :amajig_id => 2)
130
+ expect(thing.name).to eq 'Thing One'
131
+ expect(thing.id).to eq 1
132
+ end
133
+ end
134
+
135
+ describe '.find(id)' do
136
+ before do
137
+ stub_request(:get, 'http://example.com/rest/things/1.json').to_return(body:thing_hash.to_json)
138
+ end
139
+
140
+ it 'should return thing with correct attributes' do
141
+ thing = Thing.find(1)
142
+ expect(thing.id).to eq 1
143
+ expect(thing.name).to eq 'Thing One'
144
+ end
145
+
146
+ context 'when service is down' do
147
+ it 'should return approprate error' do
148
+ stub_request(:get, 'http://example.com/rest/things/1.json').
149
+ to_raise(Faraday::Error::ConnectionFailed.new('Connection Failed'))
150
+
151
+ expect{Thing.find(1)}.to raise_error(Plaza::ConnectionError)
152
+ end
153
+ end
154
+ end
155
+
156
+ describe '.where()' do
157
+ it 'should attempt to get list of things that meet where clause' do
158
+ stub_request(:get, 'http://example.com/rest/things.json?name=fred&id=3').to_return(body:things_hash.to_json)
159
+ things = Thing.where(name:'fred', id:3)
160
+ things.count.should == 2
161
+ end
162
+ end
163
+
164
+ describe '#delete' do
165
+ it 'should attempt to delete object with same id' do
166
+ stub_request(:delete, 'http://example.com/rest/things/3.json').to_return(body:thing_hash.to_json)
167
+ thing = Thing.new(id:3, name:'Cake')
168
+ thing.delete
169
+ end
170
+ end
171
+
172
+ describe '#save' do
173
+ context 'when thing does not have id' do
174
+ it 'should update it' do
175
+ return_hash = {'thing' => {'id' => 42, 'name' => 'New Thing', 'amajig_id'=>2 }}
176
+ stub_request(:post, 'http://example.com/rest/things.json').to_return(body: return_hash.to_json)
177
+ thing = Thing.new(name:'New Thing')
178
+ thing.save
179
+ expect(thing.id).to eq 42
180
+ end
181
+
182
+ end
183
+
184
+ context 'when thing has an id' do
185
+ it 'should attempt to PUT update thing with things id' do
186
+ stub_request(:put, 'http://example.com/rest/things/1.json').with(body: thing_hash.to_json).to_return(body: thing_hash.to_json)
187
+ thing = Thing.new(thing_hash['thing'])
188
+ thing.save
189
+ end
190
+ end
191
+ end
192
+
193
+ describe 'update attributes' do
194
+ it 'should update the attributes' do
195
+ thing = Thing.new(thing_hash['thing'])
196
+ stub_request(:put, "http://example.com/rest/things/1.json").
197
+ with(:body => "{\"thing\":{\"id\":1,\"name\":\"Thing Two\",\"amajig_id\":2}}").
198
+ to_return(:status => 200, :body => "{\"thing\":{\"id\":1,\"name\":\"Thing Two\",\"amajig_id\":2}}", :headers => {})
199
+ thing.update_attributes({:name => "Thing Two"})
200
+ thing.name.should == "Thing Two"
201
+ end
202
+ end
203
+
204
+ describe 'belongs_to relations' do
205
+ it 'get related object' do
206
+ thing = Thing.new(thing_hash['thing'])
207
+ stub_request(:get, 'http://example.com/rest/amajigs/2.json').to_return(body:amajig_hash.to_json)
208
+ thing.amajig.name.should == 'Jigger'
209
+ end
210
+ end
211
+
212
+ describe 'has_many relationships' do
213
+ it 'gets first has_many relationships' do
214
+ amajig = Amajig.new(amajig_hash['amajig'])
215
+ stub_request(:get, 'http://example.com/rest/amajigs/2/things.json').to_return(body:things_hash.to_json)
216
+ expect(amajig.things.first.class).to be Thing
217
+ end
218
+
219
+ it 'gets second has_many relationships' do
220
+ amajig = Amajig.new(amajig_hash['amajig'])
221
+ stub_request(:get, 'http://example.com/rest/amajigs/2/amabobs.json').to_return(body:amabobs_hash.to_json)
222
+ expect(amajig.amabobs.first.class).to be Amabob
223
+ end
224
+ end
225
+
226
+ describe 'error messages' do
227
+ it 'should return an empty array' do
228
+ thing = Thing.new(thing_hash['thing'])
229
+ thing.stubs(:errors).returns({})
230
+ thing.error_messages == {}
231
+ end
232
+
233
+ it 'should return an error message' do
234
+ thing = Thing.new(thing_hash['thing'])
235
+ thing.stubs(:errors).returns({:kpi => ["no name"]})
236
+ thing.error_messages == ["kpi no name"]
237
+ end
238
+ end
239
+
240
+ describe 'symbolize keys' do
241
+ it 'should return symbolized keys' do
242
+ thing = Thing.new(thing_hash['thing'])
243
+ thing.symbolize_keys({"id" => 2, "name" => "test"}).should == {:id=>2, :name=>"test"}
244
+ end
245
+ end
246
+
247
+ end
248
+
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+ include Plaza
3
+
4
+ describe Request do
5
+
6
+ context 'when Thread.current[:x_user_id] is set' do
7
+
8
+ before do Thread.current[:x_user_id] = 4242 end
9
+
10
+ %i(get post put delete).each do |method|
11
+ it "##{method} should add X-User-Id to headers" do
12
+ stub_request(method, "http://example.com/foobar").
13
+ with(:headers => {
14
+ 'Accept'=>'application/json',
15
+ 'X-User-Id'=>'4242'}).
16
+ to_return(:status => 200, :body => "", :headers => {})
17
+
18
+ Request.new.send(method, '/foobar')
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ context 'when Thread.current[:x_user_id] is not set' do
25
+
26
+ %i(get post put delete).each do |method|
27
+ it "##{method} should now add X-User-Id to headers" do
28
+ stub_request(method, "http://example.com/foobar").
29
+ with(:headers => {
30
+ 'Accept'=>'application/json',
31
+ 'User-Agent'=>'Faraday v0.9.0'}).
32
+ to_return(:status => 200, :body => "", :headers => {})
33
+
34
+ Request.new.send(method, '/foobar')
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ context "when trucker service is down" do
41
+ let(:request){
42
+ exception = Faraday::Adapter::Test::Stubs.new do |stub|
43
+ %i(get put post delete).each do |method|
44
+ stub.send(method, '/failblog') {raise Faraday::Error::ConnectionFailed.new('Connection Failed')}
45
+ end
46
+ end
47
+ request = Request.new do |conn|
48
+ conn.adapter :test, exception
49
+ end
50
+ request
51
+ }
52
+ %i(get put post delete).each do |method|
53
+ describe "#{method}" do
54
+ let(:response){request.send(method, '/failblog')}
55
+
56
+ it 'response code should be 503' do
57
+ expect{response}.to raise_error(Plaza::ConnectionError)
58
+ end
59
+
60
+ it 'should have error message' do
61
+ expect{response}.to raise_error { |error|
62
+ error.should be_a(Plaza::ConnectionError)
63
+ error.message.should == "Service is not available."
64
+ }
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ end
71
+
@@ -0,0 +1,40 @@
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"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ require 'simplecov'
9
+ SimpleCov.start do
10
+ add_filter '/spec/'
11
+ end
12
+
13
+ require_relative '../lib/plaza'
14
+ require 'webmock/rspec'
15
+ require 'pry'
16
+ require 'json'
17
+
18
+ class NullLogger
19
+ %i(debug info warn error fatal).each do |m|
20
+ define_method m, ->(*) {}
21
+ end
22
+ end
23
+
24
+ Plaza.configure do
25
+ base_url "http://example.com/rest"
26
+ logger NullLogger.new # Specs should STFU.
27
+ end
28
+
29
+ RSpec.configure do |config|
30
+ config.treat_symbols_as_metadata_keys_with_true_values = true
31
+ config.run_all_when_everything_filtered = true
32
+ config.filter_run :focus
33
+ config.mock_framework = :mocha
34
+
35
+ # Run specs in random order to surface order dependencies. If you find an
36
+ # order dependency and want to debug it, you can fix the order by providing
37
+ # the seed, which is printed after each run.
38
+ # --seed 1234
39
+ config.order = 'random'
40
+ end
metadata CHANGED
@@ -1,58 +1,189 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plaza
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
- - Benjamin Guest
7
+ - VMC Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-18 00:00:00.000000000 Z
11
+ date: 2014-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.7'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.7'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
33
  version: '10.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.99'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.99'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mocha
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rest-client
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '1.6'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '1.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rest-client-components
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: faraday
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 0.9.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 0.9.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: faraday_middleware
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 0.9.1
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: 0.9.1
125
+ - !ruby/object:Gem::Dependency
126
+ name: rack-cache
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ~>
130
+ - !ruby/object:Gem::Version
131
+ version: '1.2'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ~>
137
+ - !ruby/object:Gem::Version
138
+ version: '1.2'
139
+ - !ruby/object:Gem::Dependency
140
+ name: virtus
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ~>
144
+ - !ruby/object:Gem::Version
145
+ version: '1.0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ~>
151
+ - !ruby/object:Gem::Version
152
+ version: '1.0'
41
153
  description: Rest client for that works in conjuntion with rest_area gem
42
154
  email:
155
+ - eng@visiblemeasures.com
43
156
  - benguest@gmail.com
44
157
  executables: []
45
158
  extensions: []
46
159
  extra_rdoc_files: []
47
160
  files:
48
- - ".gitignore"
161
+ - .gitignore
49
162
  - Gemfile
50
163
  - LICENSE.txt
51
164
  - README.md
52
165
  - Rakefile
53
166
  - lib/plaza.rb
167
+ - lib/plaza/adapters.rb
168
+ - lib/plaza/adapters/base_adapter.rb
169
+ - lib/plaza/adapters/restful_adapter.rb
170
+ - lib/plaza/configuration.rb
171
+ - lib/plaza/inflector.rb
172
+ - lib/plaza/middleware/exceptions.rb
173
+ - lib/plaza/middleware/user_id.rb
174
+ - lib/plaza/models.rb
175
+ - lib/plaza/models/base_model.rb
176
+ - lib/plaza/models/error.rb
177
+ - lib/plaza/models/restful_model.rb
178
+ - lib/plaza/request.rb
179
+ - lib/plaza/response.rb
54
180
  - lib/plaza/version.rb
55
181
  - plaza.gemspec
182
+ - spec/plaza/adapters/restful_adapter_spec.rb
183
+ - spec/plaza/inflector_spec.rb
184
+ - spec/plaza/models/restful_model_spec.rb
185
+ - spec/plaza/request_spec.rb
186
+ - spec/spec_helper.rb
56
187
  homepage: ''
57
188
  licenses:
58
189
  - MIT
@@ -63,18 +194,23 @@ require_paths:
63
194
  - lib
64
195
  required_ruby_version: !ruby/object:Gem::Requirement
65
196
  requirements:
66
- - - ">="
197
+ - - '>='
67
198
  - !ruby/object:Gem::Version
68
199
  version: '0'
69
200
  required_rubygems_version: !ruby/object:Gem::Requirement
70
201
  requirements:
71
- - - ">="
202
+ - - '>='
72
203
  - !ruby/object:Gem::Version
73
204
  version: '0'
74
205
  requirements: []
75
206
  rubyforge_project:
76
- rubygems_version: 2.2.2
207
+ rubygems_version: 2.4.4
77
208
  signing_key:
78
209
  specification_version: 4
79
210
  summary: Client for rest_area gem
80
- test_files: []
211
+ test_files:
212
+ - spec/plaza/adapters/restful_adapter_spec.rb
213
+ - spec/plaza/inflector_spec.rb
214
+ - spec/plaza/models/restful_model_spec.rb
215
+ - spec/plaza/request_spec.rb
216
+ - spec/spec_helper.rb