plaza 0.0.1 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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