namely 0.0.1
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 +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +51 -0
- data/LICENSE +22 -0
- data/LICENSE.txt +22 -0
- data/README.md +125 -0
- data/Rakefile +16 -0
- data/lib/namely.rb +114 -0
- data/lib/namely/authenticator.rb +164 -0
- data/lib/namely/country.rb +9 -0
- data/lib/namely/currency_type.rb +9 -0
- data/lib/namely/event.rb +9 -0
- data/lib/namely/exceptions.rb +13 -0
- data/lib/namely/field.rb +9 -0
- data/lib/namely/job_tier.rb +9 -0
- data/lib/namely/profile.rb +13 -0
- data/lib/namely/report.rb +9 -0
- data/lib/namely/resource_gateway.rb +81 -0
- data/lib/namely/restful_model.rb +150 -0
- data/lib/namely/version.rb +3 -0
- data/namely.gemspec +30 -0
- data/spec/fixtures/vcr_cassettes/country_head.yml +51 -0
- data/spec/fixtures/vcr_cassettes/country_head_missing.yml +50 -0
- data/spec/fixtures/vcr_cassettes/country_index.yml +121 -0
- data/spec/fixtures/vcr_cassettes/country_show.yml +68 -0
- data/spec/fixtures/vcr_cassettes/country_show_missing.yml +54 -0
- data/spec/fixtures/vcr_cassettes/currencytype_index.yml +64 -0
- data/spec/fixtures/vcr_cassettes/event_head.yml +51 -0
- data/spec/fixtures/vcr_cassettes/event_head_missing.yml +50 -0
- data/spec/fixtures/vcr_cassettes/event_index.yml +88 -0
- data/spec/fixtures/vcr_cassettes/event_show.yml +62 -0
- data/spec/fixtures/vcr_cassettes/event_show_missing.yml +54 -0
- data/spec/fixtures/vcr_cassettes/field_index.yml +207 -0
- data/spec/fixtures/vcr_cassettes/jobtier_index.yml +103 -0
- data/spec/fixtures/vcr_cassettes/profile_create.yml +85 -0
- data/spec/fixtures/vcr_cassettes/profile_create_failed.yml +54 -0
- data/spec/fixtures/vcr_cassettes/profile_head.yml +51 -0
- data/spec/fixtures/vcr_cassettes/profile_head_missing.yml +50 -0
- data/spec/fixtures/vcr_cassettes/profile_index.yml +979 -0
- data/spec/fixtures/vcr_cassettes/profile_show.yml +91 -0
- data/spec/fixtures/vcr_cassettes/profile_show_missing.yml +54 -0
- data/spec/fixtures/vcr_cassettes/profile_show_updated.yml +91 -0
- data/spec/fixtures/vcr_cassettes/profile_update.yml +95 -0
- data/spec/fixtures/vcr_cassettes/profile_update_revert.yml +95 -0
- data/spec/fixtures/vcr_cassettes/report_head.yml +51 -0
- data/spec/fixtures/vcr_cassettes/report_head_missing.yml +50 -0
- data/spec/fixtures/vcr_cassettes/report_show.yml +185 -0
- data/spec/fixtures/vcr_cassettes/report_show_missing.yml +54 -0
- data/spec/fixtures/vcr_cassettes/token.yml +57 -0
- data/spec/fixtures/vcr_cassettes/token_refresh.yml +57 -0
- data/spec/namely/authenticator_spec.rb +143 -0
- data/spec/namely/configuration_spec.rb +33 -0
- data/spec/namely/country_spec.rb +11 -0
- data/spec/namely/currency_type_spec.rb +5 -0
- data/spec/namely/event_spec.rb +11 -0
- data/spec/namely/field_spec.rb +5 -0
- data/spec/namely/job_tier_spec.rb +5 -0
- data/spec/namely/profile_spec.rb +25 -0
- data/spec/namely/report_spec.rb +8 -0
- data/spec/namely/resource_gateway_spec.rb +93 -0
- data/spec/shared_examples/a_model_with_a_create_action.rb +24 -0
- data/spec/shared_examples/a_model_with_a_show_action.rb +38 -0
- data/spec/shared_examples/a_model_with_an_index_action.rb +17 -0
- data/spec/shared_examples/a_model_with_an_update_action.rb +37 -0
- data/spec/spec_helper.rb +49 -0
- metadata +280 -0
    
        data/lib/namely/event.rb
    ADDED
    
    
    
        data/lib/namely/field.rb
    ADDED
    
    
| @@ -0,0 +1,81 @@ | |
| 1 | 
            +
            module Namely
         | 
| 2 | 
            +
              class ResourceGateway
         | 
| 3 | 
            +
                def initialize(options)
         | 
| 4 | 
            +
                  @access_token = options.fetch(:access_token)
         | 
| 5 | 
            +
                  @endpoint = options.fetch(:endpoint)
         | 
| 6 | 
            +
                  @resource_name = options.fetch(:resource_name)
         | 
| 7 | 
            +
                  @subdomain = options.fetch(:subdomain)
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def json_index
         | 
| 11 | 
            +
                  get("/#{endpoint}", limit: :all)[resource_name]
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def json_show(id)
         | 
| 15 | 
            +
                  get("/#{endpoint}/#{id}")[resource_name].first
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def show_head(id)
         | 
| 19 | 
            +
                  head("/#{endpoint}/#{id}")
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def create(attributes)
         | 
| 23 | 
            +
                  response = post(
         | 
| 24 | 
            +
                    "/#{endpoint}",
         | 
| 25 | 
            +
                    endpoint => [attributes]
         | 
| 26 | 
            +
                  )
         | 
| 27 | 
            +
                  extract_id(response)
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def update(id, changes)
         | 
| 31 | 
            +
                  put("/#{endpoint}/#{id}", endpoint => [changes])
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                private
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                attr_reader :access_token, :endpoint, :resource_name, :subdomain
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def url(path)
         | 
| 39 | 
            +
                  "https://#{subdomain}.namely.com/api/v1#{path}"
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def extract_id(response)
         | 
| 43 | 
            +
                  JSON.parse(response)[endpoint].first["id"]
         | 
| 44 | 
            +
                rescue StandardError => e
         | 
| 45 | 
            +
                  raise(
         | 
| 46 | 
            +
                    FailedRequestError,
         | 
| 47 | 
            +
                    "Couldn't parse \"id\" from response: #{e.message}"
         | 
| 48 | 
            +
                  )
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def get(path, params = {})
         | 
| 52 | 
            +
                  params.merge!(access_token: access_token)
         | 
| 53 | 
            +
                  JSON.parse(RestClient.get(url(path), accept: :json, params: params))
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def head(path, params = {})
         | 
| 57 | 
            +
                  params.merge!(access_token: access_token)
         | 
| 58 | 
            +
                  RestClient.head(url(path), accept: :json, params: params)
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def post(path, params)
         | 
| 62 | 
            +
                  params.merge!(access_token: access_token)
         | 
| 63 | 
            +
                  RestClient.post(
         | 
| 64 | 
            +
                    url(path),
         | 
| 65 | 
            +
                    params.to_json,
         | 
| 66 | 
            +
                    accept: :json,
         | 
| 67 | 
            +
                    content_type: :json,
         | 
| 68 | 
            +
                  )
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def put(path, params)
         | 
| 72 | 
            +
                  params.merge!(access_token: access_token)
         | 
| 73 | 
            +
                  RestClient.put(
         | 
| 74 | 
            +
                    url(path),
         | 
| 75 | 
            +
                    params.to_json,
         | 
| 76 | 
            +
                    accept: :json,
         | 
| 77 | 
            +
                    content_type: :json
         | 
| 78 | 
            +
                  )
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
            end
         | 
| @@ -0,0 +1,150 @@ | |
| 1 | 
            +
            module Namely
         | 
| 2 | 
            +
              # @abstract
         | 
| 3 | 
            +
              class RestfulModel < OpenStruct
         | 
| 4 | 
            +
                # Fetch a model from the server by its ID.
         | 
| 5 | 
            +
                #
         | 
| 6 | 
            +
                # @param [#to_s] id
         | 
| 7 | 
            +
                #
         | 
| 8 | 
            +
                # @raise [NoSuchModelError] if the model wasn't found.
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # @return [RestfulModel]
         | 
| 11 | 
            +
                def self.find(id)
         | 
| 12 | 
            +
                  new(resource_gateway.json_show(id))
         | 
| 13 | 
            +
                rescue RestClient::ResourceNotFound
         | 
| 14 | 
            +
                  raise NoSuchModelError, "Can't find a #{name} with id \"#{id}\""
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                # Returns true if a model with this ID exists, false otherwise.
         | 
| 18 | 
            +
                #
         | 
| 19 | 
            +
                # @param [#to_s] id
         | 
| 20 | 
            +
                #
         | 
| 21 | 
            +
                # @return [Boolean]
         | 
| 22 | 
            +
                def self.exists?(id)
         | 
| 23 | 
            +
                  resource_gateway.show_head(id)
         | 
| 24 | 
            +
                  true
         | 
| 25 | 
            +
                rescue RestClient::ResourceNotFound
         | 
| 26 | 
            +
                  false
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                # Return every instance of this model.
         | 
| 30 | 
            +
                #
         | 
| 31 | 
            +
                # A model might have quite a few instances. If this is the case,
         | 
| 32 | 
            +
                # the query may take some time (several seconds) and the resulting
         | 
| 33 | 
            +
                # array may be very large.
         | 
| 34 | 
            +
                #
         | 
| 35 | 
            +
                # @return [Array<RestfulModel>]
         | 
| 36 | 
            +
                def self.all
         | 
| 37 | 
            +
                  resource_gateway.json_index.map { |model| new(model) }
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                # Create a new model on the server with the given attributes.
         | 
| 41 | 
            +
                #
         | 
| 42 | 
            +
                # @param [Hash] attributes the attributes of the model being created.
         | 
| 43 | 
            +
                #
         | 
| 44 | 
            +
                # @example
         | 
| 45 | 
            +
                #   Profile.create!(
         | 
| 46 | 
            +
                #     first_name: "Beardsly",
         | 
| 47 | 
            +
                #     last_name: "McDog",
         | 
| 48 | 
            +
                #     email: "beardsly@namely.com"
         | 
| 49 | 
            +
                #   )
         | 
| 50 | 
            +
                #
         | 
| 51 | 
            +
                # @return [RestfulModel] the created model.
         | 
| 52 | 
            +
                def self.create!(attributes)
         | 
| 53 | 
            +
                  new(attributes).save!
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                # Update the attributes of this model. Assign the attributes
         | 
| 57 | 
            +
                # according to the hash, then persist those changes on the server.
         | 
| 58 | 
            +
                #
         | 
| 59 | 
            +
                # @param [Hash] attributes the attributes to be updated on the model.
         | 
| 60 | 
            +
                #
         | 
| 61 | 
            +
                # @example
         | 
| 62 | 
            +
                #   my_profile.update(
         | 
| 63 | 
            +
                #     middle_name: "Ludwig"
         | 
| 64 | 
            +
                #   )
         | 
| 65 | 
            +
                #
         | 
| 66 | 
            +
                # @raise [FailedRequestError] if the request failed for any reason.
         | 
| 67 | 
            +
                #
         | 
| 68 | 
            +
                # @return [RestfulModel] the updated model.
         | 
| 69 | 
            +
                def update(attributes)
         | 
| 70 | 
            +
                  attributes.each do |key, value|
         | 
| 71 | 
            +
                    self[key] = value
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  persist_model_changes(attributes)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  self
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                # Try to persist the current object, either by creating a new
         | 
| 80 | 
            +
                # object on the server or by updating an existing one. Raise an
         | 
| 81 | 
            +
                # error if the object can't be saved.
         | 
| 82 | 
            +
                #
         | 
| 83 | 
            +
                # @raise [FailedRequestError] if the request failed for any reason.
         | 
| 84 | 
            +
                #
         | 
| 85 | 
            +
                # @return [RestfulModel] the model itself, if saving succeeded.
         | 
| 86 | 
            +
                def save!
         | 
| 87 | 
            +
                  if persisted?
         | 
| 88 | 
            +
                    update(to_h)
         | 
| 89 | 
            +
                  else
         | 
| 90 | 
            +
                    self.id = resource_gateway.create(to_h)
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                  self
         | 
| 93 | 
            +
                rescue RestClient::Exception => e
         | 
| 94 | 
            +
                  raise FailedRequestError, e.message
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                # Return true if the model exists (in some state) on the server.
         | 
| 98 | 
            +
                #
         | 
| 99 | 
            +
                # @return [Boolean]
         | 
| 100 | 
            +
                def persisted?
         | 
| 101 | 
            +
                  !!id
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                private
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                def resource_gateway
         | 
| 107 | 
            +
                  self.class.resource_gateway
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                def self.resource_gateway
         | 
| 111 | 
            +
                  Namely.resource_gateway(resource_name, endpoint)
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                def resource_gateway
         | 
| 115 | 
            +
                  self.class.resource_gateway
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def self.endpoint
         | 
| 119 | 
            +
                  raise(
         | 
| 120 | 
            +
                    NotImplementedError,
         | 
| 121 | 
            +
                    "Namely::Model subclasses must define an `.endpoint` "\
         | 
| 122 | 
            +
                    "class method that returns their path in the API."
         | 
| 123 | 
            +
                  )
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                def self.resource_name
         | 
| 127 | 
            +
                  endpoint.split("/").last
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                # The update action for certain models requires populating certain
         | 
| 131 | 
            +
                # fields. Without populating these fields, an update with
         | 
| 132 | 
            +
                # fail. This method is intended to be overwritten by subclasses
         | 
| 133 | 
            +
                # with required keys.
         | 
| 134 | 
            +
                #
         | 
| 135 | 
            +
                # @return [Array<Symbol>]
         | 
| 136 | 
            +
                def required_keys_for_update
         | 
| 137 | 
            +
                  []
         | 
| 138 | 
            +
                end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                def required_attributes_for_update
         | 
| 141 | 
            +
                  to_h.select { |key, _| required_keys_for_update.include?(key) }
         | 
| 142 | 
            +
                end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                def persist_model_changes(changed_attributes)
         | 
| 145 | 
            +
                  resource_gateway.update(id, changed_attributes.merge(required_attributes_for_update))
         | 
| 146 | 
            +
                rescue RestClient::Exception => e
         | 
| 147 | 
            +
                  raise FailedRequestError, e.message
         | 
| 148 | 
            +
                end
         | 
| 149 | 
            +
              end
         | 
| 150 | 
            +
            end
         | 
    
        data/namely.gemspec
    ADDED
    
    | @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            # coding: utf-8
         | 
| 2 | 
            +
            lib = File.expand_path('../lib', __FILE__)
         | 
| 3 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 4 | 
            +
            require "namely/version"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Gem::Specification.new do |spec|
         | 
| 7 | 
            +
              spec.name          = "namely"
         | 
| 8 | 
            +
              spec.version       = Namely::VERSION
         | 
| 9 | 
            +
              spec.authors       = ["Harry Schwartz"]
         | 
| 10 | 
            +
              spec.email         = ["harry@thoughtbot.com"]
         | 
| 11 | 
            +
              spec.summary       = "Wraps the Namely HTTP API in lovely Ruby."
         | 
| 12 | 
            +
              spec.homepage      = "https://github.com/namely/ruby-client"
         | 
| 13 | 
            +
              spec.license       = "MIT"
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              spec.files         = `git ls-files -z`.split("\x0")
         | 
| 16 | 
            +
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         | 
| 17 | 
            +
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         | 
| 18 | 
            +
              spec.require_paths = ["lib"]
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              spec.add_dependency "backports"
         | 
| 21 | 
            +
              spec.add_dependency "rest_client"
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              spec.add_development_dependency "bundler", "~> 1.7"
         | 
| 24 | 
            +
              spec.add_development_dependency "dotenv"
         | 
| 25 | 
            +
              spec.add_development_dependency "rake", "~> 10.0"
         | 
| 26 | 
            +
              spec.add_development_dependency "rspec"
         | 
| 27 | 
            +
              spec.add_development_dependency "vcr"
         | 
| 28 | 
            +
              spec.add_development_dependency "webmock"
         | 
| 29 | 
            +
              spec.add_development_dependency "yard"
         | 
| 30 | 
            +
            end
         | 
| @@ -0,0 +1,51 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            http_interactions:
         | 
| 3 | 
            +
            - request:
         | 
| 4 | 
            +
                method: head
         | 
| 5 | 
            +
                uri: https://<TEST_SUBDOMAIN>.namely.com/api/v1/countries/US?access_token=<TEST_ACCESS_TOKEN>
         | 
| 6 | 
            +
                body:
         | 
| 7 | 
            +
                  encoding: US-ASCII
         | 
| 8 | 
            +
                  string: ''
         | 
| 9 | 
            +
                headers:
         | 
| 10 | 
            +
                  Accept:
         | 
| 11 | 
            +
                  - application/json
         | 
| 12 | 
            +
                  Accept-Encoding:
         | 
| 13 | 
            +
                  - gzip, deflate
         | 
| 14 | 
            +
                  User-Agent:
         | 
| 15 | 
            +
                  - Ruby
         | 
| 16 | 
            +
              response:
         | 
| 17 | 
            +
                status:
         | 
| 18 | 
            +
                  code: 200
         | 
| 19 | 
            +
                  message: OK
         | 
| 20 | 
            +
                headers:
         | 
| 21 | 
            +
                  Cache-Control:
         | 
| 22 | 
            +
                  - max-age=0, private, must-revalidate
         | 
| 23 | 
            +
                  Content-Encoding:
         | 
| 24 | 
            +
                  - gzip
         | 
| 25 | 
            +
                  Content-Type:
         | 
| 26 | 
            +
                  - application/json; charset=utf-8
         | 
| 27 | 
            +
                  Date:
         | 
| 28 | 
            +
                  - Tue, 04 Nov 2014 16:34:36 GMT
         | 
| 29 | 
            +
                  Server:
         | 
| 30 | 
            +
                  - nginx/1.6.2
         | 
| 31 | 
            +
                  Status:
         | 
| 32 | 
            +
                  - 200 OK
         | 
| 33 | 
            +
                  Strict-Transport-Security:
         | 
| 34 | 
            +
                  - max-age=31536000
         | 
| 35 | 
            +
                  - max-age=31536000; includeSubDomains;
         | 
| 36 | 
            +
                  Vary:
         | 
| 37 | 
            +
                  - Accept-Encoding
         | 
| 38 | 
            +
                  X-Rack-Cache:
         | 
| 39 | 
            +
                  - miss
         | 
| 40 | 
            +
                  X-Request-Id:
         | 
| 41 | 
            +
                  - 12b71052-a0c4-4776-86ee-417d8f3b87dd
         | 
| 42 | 
            +
                  X-Runtime:
         | 
| 43 | 
            +
                  - '0.021096'
         | 
| 44 | 
            +
                  Connection:
         | 
| 45 | 
            +
                  - keep-alive
         | 
| 46 | 
            +
                body:
         | 
| 47 | 
            +
                  encoding: UTF-8
         | 
| 48 | 
            +
                  string: ''
         | 
| 49 | 
            +
                http_version: 
         | 
| 50 | 
            +
              recorded_at: Tue, 04 Nov 2014 16:34:36 GMT
         | 
| 51 | 
            +
            recorded_with: VCR 2.9.3
         | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            http_interactions:
         | 
| 3 | 
            +
            - request:
         | 
| 4 | 
            +
                method: head
         | 
| 5 | 
            +
                uri: https://<TEST_SUBDOMAIN>.namely.com/api/v1/countries/this-is-almost-certainly-not-the-id-of-any-model?access_token=<TEST_ACCESS_TOKEN>
         | 
| 6 | 
            +
                body:
         | 
| 7 | 
            +
                  encoding: US-ASCII
         | 
| 8 | 
            +
                  string: ''
         | 
| 9 | 
            +
                headers:
         | 
| 10 | 
            +
                  Accept:
         | 
| 11 | 
            +
                  - application/json
         | 
| 12 | 
            +
                  Accept-Encoding:
         | 
| 13 | 
            +
                  - gzip, deflate
         | 
| 14 | 
            +
                  User-Agent:
         | 
| 15 | 
            +
                  - Ruby
         | 
| 16 | 
            +
              response:
         | 
| 17 | 
            +
                status:
         | 
| 18 | 
            +
                  code: 404
         | 
| 19 | 
            +
                  message: Not Found
         | 
| 20 | 
            +
                headers:
         | 
| 21 | 
            +
                  Cache-Control:
         | 
| 22 | 
            +
                  - no-cache
         | 
| 23 | 
            +
                  Content-Encoding:
         | 
| 24 | 
            +
                  - gzip
         | 
| 25 | 
            +
                  Content-Type:
         | 
| 26 | 
            +
                  - application/json; charset=utf-8
         | 
| 27 | 
            +
                  Date:
         | 
| 28 | 
            +
                  - Tue, 04 Nov 2014 16:34:36 GMT
         | 
| 29 | 
            +
                  Server:
         | 
| 30 | 
            +
                  - nginx
         | 
| 31 | 
            +
                  Status:
         | 
| 32 | 
            +
                  - 404 Not Found
         | 
| 33 | 
            +
                  Strict-Transport-Security:
         | 
| 34 | 
            +
                  - max-age=31536000
         | 
| 35 | 
            +
                  Vary:
         | 
| 36 | 
            +
                  - Accept-Encoding
         | 
| 37 | 
            +
                  X-Rack-Cache:
         | 
| 38 | 
            +
                  - miss
         | 
| 39 | 
            +
                  X-Request-Id:
         | 
| 40 | 
            +
                  - 15c2c8fd-5244-48b7-9f18-040318b7e9ac
         | 
| 41 | 
            +
                  X-Runtime:
         | 
| 42 | 
            +
                  - '0.026183'
         | 
| 43 | 
            +
                  Connection:
         | 
| 44 | 
            +
                  - keep-alive
         | 
| 45 | 
            +
                body:
         | 
| 46 | 
            +
                  encoding: UTF-8
         | 
| 47 | 
            +
                  string: ''
         | 
| 48 | 
            +
                http_version: 
         | 
| 49 | 
            +
              recorded_at: Tue, 04 Nov 2014 16:34:36 GMT
         | 
| 50 | 
            +
            recorded_with: VCR 2.9.3
         |