Comparing changes

v0.2.16 v0.3.0
30 commits 24 files changed

Commits

7b70879 release 0.3.0 mokha 2019-02-22 01:42:10
3d23d9e run ruby 2.6.1 in CI. mokha 2019-02-22 01:37:27
4b8607e fix linter errors mokha 2019-02-22 01:35:50
3a124df use scim content type in headers mokha 2019-02-16 22:14:20
2cbaa80 write documentation mokha 2019-02-15 01:23:08
a0378a6 extract method to remove duplication mokha 2019-02-13 21:22:00
d88865d update README mokha 2019-02-13 21:11:54
9c3cc37 add badges mokha 2019-02-13 01:00:55
21c746e extract methods mokha 2019-02-13 00:55:17
2a5c9f4 load remote resource types mokha 2019-02-13 00:52:24
06f1481 update CHANGELOG mokha 2019-02-13 00:18:54
c5dba77 fix linter errors mokha 2019-02-13 00:15:37
7dbdeee delegate to configuration object. mokha 2019-02-13 00:13:21
7ff29d5 parse the resource type mokha 2019-02-12 23:19:19
93effe8 parse the resource type json mokha 2019-02-12 23:17:01
2cffcd0 fix linter errors mokha 2019-02-12 18:45:05
1765137 parse authentication schemes mokha 2019-02-12 18:12:08
c9558cd fix linter errors mokha 2019-02-12 17:19:01
f595a04 extract parse_date method mokha 2019-02-12 02:14:00
9550900 fix some linter errors mokha 2019-02-12 02:03:11
2fc11e3 parse the attributes traits mokha 2019-02-12 01:57:48
f91962d start to load schema from json mokha 2019-02-12 00:00:52
lib/scim/kit/v2/attributable.rb
@@ -7,14 +7,21 @@ module Scim
       module Attributable
         include Enumerable
 
+        # Returns a hash of the generated dynamic attributes
+        # @return [Hash] the dynamic attributes keys by their name
         def dynamic_attributes
           @dynamic_attributes ||= {}.with_indifferent_access
         end
 
+        # Defines dynamic attributes on the resource for the types provided
+        # @param resource [Scim::Kit::V2::Resource] the resource to attach dynamic attributes to.
+        # @param types [Array<Scim::Kit::V2::AttributeType>] the array of types
         def define_attributes_for(resource, types)
           types.each { |x| attribute(x, resource) }
         end
 
+        # Assigns attribute values via the provided hash.
+        # @param attributes [Hash] The name/values to assign.
         def assign_attributes(attributes = {})
           attributes.each do |key, value|
             next if key.to_sym == :schemas
@@ -27,10 +34,16 @@ module Scim
           end
         end
 
+        # Returns the attribute identified by the name.
+        # @param name [String] the name of the attribute to return
+        # @return [Scim::Kit::V2::Attribute] the attribute or {Scim::Kit::V2::UnknownAttribute}
         def attribute_for(name)
           dynamic_attributes[name.to_s.underscore] || UnknownAttribute.new(name)
         end
 
+        # Returns the value associated with the attribute name
+        # @param name [String] the name of the attribute
+        # @return [Object] the value assigned to the attribute
         def read_attribute(name)
           attribute = attribute_for(name)
           return attribute._value if attribute._type.multi_valued
@@ -38,6 +51,9 @@ module Scim
           attribute._type.complex? ? attribute : attribute._value
         end
 
+        # Assigns the value to the attribute with the given name
+        # @param name [String] the name of the attribute
+        # @param value [Object] the value to assign to the attribute
         def write_attribute(name, value)
           if value.is_a?(Hash)
             attribute_for(name)&.assign_attributes(value)
@@ -46,6 +62,8 @@ module Scim
           end
         end
 
+        # yields each attribute to the provided block
+        # @param [Block] the block to yield each attribute to.
         def each
           dynamic_attributes.each do |_name, attribute|
             yield attribute
lib/scim/kit/v2/attribute_type.rb
@@ -6,20 +6,14 @@ module Scim
       # Represents a scim Attribute type
       class AttributeType
         include Templatable
-        attr_accessor :canonical_values
-        attr_accessor :case_exact
-        attr_accessor :description
-        attr_accessor :multi_valued
-        attr_accessor :required
-        attr_reader :mutability
-        attr_reader :name, :type
-        attr_reader :reference_types
-        attr_reader :returned
-        attr_reader :uniqueness
+        attr_accessor :canonical_values, :case_exact, :description
+        attr_accessor :multi_valued, :required
+        attr_reader :mutability, :name, :type, :attributes
+        attr_reader :reference_types, :returned, :uniqueness
 
         def initialize(name:, type: :string)
           @name = name.to_s.underscore
-          @type = type.to_sym
+          @type = DATATYPES[type.to_sym] ? type.to_sym : (raise TYPE_ERROR)
           @description = name
           @multi_valued = false
           @required = false
@@ -27,7 +21,7 @@ module Scim
           @mutability = Mutability::READ_WRITE
           @returned = Returned::DEFAULT
           @uniqueness = Uniqueness::NONE
-          raise ArgumentError, :type unless DATATYPES[@type]
+          @attributes = []
         end
 
         def mutability=(value)
@@ -54,10 +48,6 @@ module Scim
           @reference_types = value
         end
 
-        def attributes
-          @attributes ||= []
-        end
-
         def complex?
           type_is?(:complex)
         end
@@ -85,6 +75,19 @@ module Scim
           complex? ? valid_complex?(value) : valid_simple?(value)
         end
 
+        class << self
+          def from(hash)
+            x = new(name: hash[:name], type: hash[:type])
+            %i[
+              canonicalValues caseExact description multiValued mutability
+              referenceTypes required returned uniqueness
+            ].each do |y|
+              x.public_send("#{y.to_s.underscore}=", hash[y]) if hash.key?(y)
+            end
+            x
+          end
+        end
+
         private
 
         def coerce_single(value)
lib/scim/kit/v2/authentication_scheme.rb
@@ -32,15 +32,26 @@ module Scim
           yield self if block_given?
         end
 
-        def self.build_for(type, primary: nil)
-          defaults = DEFAULTS[type.to_sym] || {}
-          new do |x|
-            x.type = type
-            x.primary = primary
-            x.description = defaults[:description]
-            x.documentation_uri = defaults[:documentation_uri]
-            x.name = defaults[:name]
-            x.spec_uri = defaults[:spec_uri]
+        class << self
+          def build_for(type, primary: nil)
+            defaults = DEFAULTS[type.to_sym] || {}
+            new do |x|
+              x.type = type
+              x.primary = primary
+              x.description = defaults[:description]
+              x.documentation_uri = defaults[:documentation_uri]
+              x.name = defaults[:name]
+              x.spec_uri = defaults[:spec_uri]
+            end
+          end
+
+          def from(hash)
+            x = build_for(hash[:type], primary: hash[:primary])
+            x.description = hash[:description]
+            x.documentation_uri = hash[:documentationUri]
+            x.name = hash[:name]
+            x.spec_uri = hash[:specUri]
+            x
           end
         end
       end
lib/scim/kit/v2/configuration.rb
@@ -7,31 +7,32 @@ module Scim
       class Configuration
         # @private
         class Builder
-          def initialize
-            @resource_types = {}
-            @schemas = {}
+          attr_reader :configuration
+
+          def initialize(configuration)
+            @configuration = configuration
           end
 
           def service_provider_configuration(location:)
-            @sp_config = ServiceProviderConfiguration.new(location: location)
-            yield @sp_config
+            configuration.service_provider_configuration =
+              ServiceProviderConfiguration.new(location: location)
+            yield configuration.service_provider_configuration
           end
 
           def resource_type(id:, location:)
-            @resource_types[id] ||= ResourceType.new(location: location)
-            @resource_types[id].id = id
-            yield @resource_types[id]
+            configuration.resource_types[id] ||=
+              ResourceType.new(location: location)
+            configuration.resource_types[id].id = id
+            yield configuration.resource_types[id]
           end
 
           def schema(id:, name:, location:)
-            @schemas[id] ||= Schema.new(id: id, name: name, location: location)
-            yield @schemas[id]
-          end
-
-          def apply_to(configuration)
-            configuration.service_provider_configuration = @sp_config
-            configuration.resource_types = @resource_types
-            configuration.schemas = @schemas
+            configuration.schemas[id] ||= Schema.new(
+              id: id,
+              name: name,
+              location: location
+            )
+            yield configuration.schemas[id]
           end
         end
 
@@ -40,9 +41,41 @@ module Scim
         attr_accessor :schemas
 
         def initialize
-          builder = Builder.new
-          yield builder if block_given?
-          builder.apply_to(self)
+          @resource_types = {}
+          @schemas = {}
+
+          yield Builder.new(self) if block_given?
+        end
+
+        def load_from(base_url)
+          uri = URI.join(base_url, 'ServiceProviderConfig')
+          self.service_provider_configuration =
+            ServiceProviderConfiguration.parse(client.get(uri).body)
+
+          load_items(base_url, 'Schemas', Schema, schemas)
+          load_items(base_url, 'ResourceTypes', ResourceType, resource_types)
+        end
+
+        private
+
+        def load_items(base_url, path, type, items)
+          response = client.get(URI.join(base_url, path), headers: headers)
+          hashes = JSON.parse(response.body, symbolize_names: true)
+          hashes.each do |hash|
+            item = type.from(hash)
+            items[item.id] = item
+          end
+        end
+
+        def client
+          @client ||= Net::Hippie::Client.new
+        end
+
+        def headers
+          {
+            'Accept' => 'application/scim+json',
+            'Content-Type' => 'application/scim+json'
+          }
         end
       end
     end
lib/scim/kit/v2/meta.rb
@@ -21,6 +21,20 @@ module Scim
         def disable_timestamps
           @version = @created = @last_modified = nil
         end
+
+        def self.from(hash)
+          meta = Meta.new(hash[:resourceType], hash[:location])
+          meta.created = parse_date(hash[:created])
+          meta.last_modified = parse_date(hash[:lastModified])
+          meta.version = hash[:version]
+          meta
+        end
+
+        def self.parse_date(date)
+          DateTime.parse(date).to_time
+        rescue StandardError
+          nil
+        end
       end
     end
   end
lib/scim/kit/v2/mutability.rb
@@ -11,10 +11,13 @@ module Scim
         WRITE_ONLY = 'writeOnly'
         VALID = {
           immutable: IMMUTABLE,
+          readOnly: READ_ONLY,
+          readWrite: READ_WRITE,
           read_only: READ_ONLY,
           read_write: READ_WRITE,
           readonly: READ_ONLY,
           readwrite: READ_WRITE,
+          writeOnly: WRITE_ONLY,
           write_only: WRITE_ONLY,
           writeonly: WRITE_ONLY
         }.freeze
lib/scim/kit/v2/resource.rb
@@ -27,6 +27,10 @@ module Scim
           yield self if block_given?
         end
 
+        # Returns the current mode.
+        #
+        # @param type [Symbol] The mode `:server` or `:client`.
+        # @return [Boolean] Returns true if the resource matches the # type of mode
         def mode?(type)
           case type.to_sym
           when :server
@@ -36,6 +40,8 @@ module Scim
           end
         end
 
+        # Returns the name of the jbuilder template file.
+        # @return [String] the name of the jbuilder template.
         def template_name
           'resource.json.jbuilder'
         end
lib/scim/kit/v2/resource_type.rb
@@ -13,7 +13,7 @@ module Scim
         attr_accessor :endpoint
         attr_accessor :schema
         attr_reader :schema_extensions
-        attr_reader :meta
+        attr_accessor :meta
 
         def initialize(location:)
           @meta = Meta.new('ResourceType', location)
@@ -25,10 +25,28 @@ module Scim
           @schema_extensions.push(schema: schema, required: required)
         end
 
-        def self.build(*args)
-          item = new(*args)
-          yield item
-          item
+        class << self
+          def build(*args)
+            item = new(*args)
+            yield item
+            item
+          end
+
+          def from(hash)
+            x = new(location: hash[:location])
+            x.meta = Meta.from(hash[:meta])
+            %i[id name description endpoint schema].each do |key|
+              x.public_send("#{key}=", hash[key])
+            end
+            hash[:schemaExtensions].each do |y|
+              x.add_schema_extension(schema: y[:schema], required: y[:required])
+            end
+            x
+          end
+
+          def parse(json)
+            from(JSON.parse(json, symbolize_names: true))
+          end
         end
       end
     end
lib/scim/kit/v2/schema.rb
@@ -7,8 +7,8 @@ module Scim
       class Schema
         include Templatable
 
-        attr_reader :id, :name, :attributes, :meta
-        attr_accessor :description
+        attr_reader :id, :name, :attributes
+        attr_accessor :meta, :description
 
         def initialize(id:, name:, location:)
           @id = id
@@ -30,10 +30,29 @@ module Scim
           id.include?(Schemas::CORE) || id.include?(Messages::CORE)
         end
 
-        def self.build(*args)
-          item = new(*args)
-          yield item
-          item
+        class << self
+          def build(*args)
+            item = new(*args)
+            yield item
+            item
+          end
+
+          def from(hash)
+            Schema.new(
+              id: hash[:id],
+              name: hash[:name],
+              location: hash[:location]
+            ) do |x|
+              x.meta = Meta.from(hash[:meta])
+              hash[:attributes].each do |y|
+                x.attributes << AttributeType.from(y)
+              end
+            end
+          end
+
+          def parse(json)
+            from(JSON.parse(json, symbolize_names: true))
+          end
         end
       end
     end
lib/scim/kit/v2/service_provider_configuration.rb
@@ -6,14 +6,16 @@ module Scim
       # Represents a scim Service Provider Configuration
       class ServiceProviderConfiguration
         include Templatable
-        attr_reader :location
-        attr_accessor :documentation_uri
-        attr_reader :authentication_schemes
-        attr_reader :etag, :sort, :change_password, :patch
-        attr_reader :bulk, :filter, :meta
+        attr_accessor :bulk, :filter
+        attr_accessor :etag, :sort, :change_password, :patch
+        attr_accessor :meta, :documentation_uri
+        attr_accessor :authentication_schemes
 
-        def initialize(location:)
-          @meta = Meta.new('ServiceProviderConfig', location)
+        def initialize(
+          location:,
+          meta: Meta.new('ServiceProviderConfig', location)
+        )
+          @meta = meta
           @authentication_schemes = []
           @etag = Supportable.new
           @sort = Supportable.new
@@ -28,6 +30,21 @@ module Scim
           yield scheme if block_given?
           @authentication_schemes << scheme
         end
+
+        class << self
+          def parse(json, hash = JSON.parse(json, symbolize_names: true))
+            x = new(location: hash[:location], meta: Meta.from(hash[:meta]))
+            x.documentation_uri = hash[:documentationUri]
+            %i[patch changePassword sort etag filter bulk].each do |key|
+              x.send("#{key.to_s.underscore}=", Supportable.from(hash[key]))
+            end
+            schemes = hash[:authenticationSchemes]
+            x.authentication_schemes = schemes&.map do |auth|
+              AuthenticationScheme.from(auth)
+            end
+            x
+          end
+        end
       end
     end
   end
lib/scim/kit/v2/supportable.rb
@@ -11,11 +11,22 @@ module Scim
         attr_accessor :supported
 
         def initialize(*dynamic_attributes)
+          dynamic_attributes.delete(:supported)
           @dynamic_attributes = Hash[
             dynamic_attributes.map { |x| ["#{x}=".to_sym, nil] }
           ]
           @supported = false
         end
+
+        class << self
+          def from(hash)
+            x = new(*hash.keys)
+            hash.each do |key, value|
+              x.public_send("#{key}=", value)
+            end
+            x
+          end
+        end
       end
     end
   end
lib/scim/kit/templatable.rb
@@ -4,22 +4,35 @@ module Scim
   module Kit
     # Implement methods necessary to generate json from jbuilder templates.
     module Templatable
+      # Returns the JSON representation of the item.
+      # @param options [Hash] the hash of options to forward to jbuilder
+      # return [String] the json string
       def to_json(options = {})
         render(self, options)
       end
 
+      # Returns the hash representation of the JSON
+      # @return [Hash] the hash representation of the items JSON.
       def as_json(_options = nil)
         to_h
       end
 
+      # Returns the hash representation of the JSON
+      # @return [Hash] the hash representation of the items JSON.
       def to_h
         JSON.parse(to_json, symbolize_names: true).with_indifferent_access
       end
 
+      # Renders the model to JSON.
+      # @param model [Object] the model to render.
+      # @param options [Hash] the hash of options to pass to jbuilder.
+      # @return [String] the JSON.
       def render(model, options)
         Template.new(model).to_json(options)
       end
 
+      # Returns the file name of the jbuilder template.
+      # @return [String] name of the jbuilder template.
       def template_name
         "#{self.class.name.split('::').last.underscore}.json.jbuilder"
       end
lib/scim/kit/version.rb
@@ -2,6 +2,6 @@
 
 module Scim
   module Kit
-    VERSION = '0.2.16'
+    VERSION = '0.3.0'
   end
 end
lib/scim/kit.rb
@@ -4,6 +4,7 @@ require 'active_model'
 require 'active_support/core_ext/hash/indifferent_access'
 require 'json'
 require 'logger'
+require 'net/hippie'
 require 'pathname'
 require 'tilt'
 require 'tilt/jbuilder'
@@ -19,6 +20,7 @@ module Scim
   module Kit
     class Error < StandardError; end
     class UnknownAttributeError < Error; end
+    TYPE_ERROR = ArgumentError.new(:type)
 
     def self.logger
       @logger ||= Logger.new(STDOUT)
spec/scim/kit/v2/configuration_spec.rb
@@ -37,4 +37,36 @@ RSpec.describe Scim::Kit::V2::Configuration do
   specify { expect(subject.schemas['User'].name).to eql('User') }
   specify { expect(subject.schemas['User'].meta.location).to eql(user_schema_location) }
   specify { expect(subject.schemas['User'].attributes[0].name).to eql('user_name') }
+
+  describe '#load_from' do
+    let(:base_url) { FFaker::Internet.uri('https') }
+    let(:service_provider_configuration) do
+      Scim::Kit::V2::ServiceProviderConfiguration.new(location: FFaker::Internet.uri('https'))
+    end
+    let(:schema) do
+      Scim::Kit::V2::Schema.new(id: 'User', name: 'User', location: FFaker::Internet.uri('https'))
+    end
+    let(:resource_type) do
+      x = Scim::Kit::V2::ResourceType.new(location: FFaker::Internet.uri('https'))
+      x.id = 'User'
+      x
+    end
+
+    before do
+      stub_request(:get, "#{base_url}/ServiceProviderConfig")
+        .to_return(status: 200, body: service_provider_configuration.to_json)
+
+      stub_request(:get, "#{base_url}/Schemas")
+        .to_return(status: 200, body: [schema.to_h].to_json)
+
+      stub_request(:get, "#{base_url}/ResourceTypes")
+        .to_return(status: 200, body: [resource_type.to_h].to_json)
+
+      subject.load_from(base_url)
+    end
+
+    specify { expect(subject.service_provider_configuration.to_h).to eql(service_provider_configuration.to_h) }
+    specify { expect(subject.schemas[schema.id].to_h).to eql(schema.to_h) }
+    specify { expect(subject.resource_types[resource_type.id].to_h).to eql(resource_type.to_h) }
+  end
 end
spec/scim/kit/v2/resource_type_spec.rb
@@ -30,4 +30,20 @@ RSpec.describe Scim::Kit::V2::ResourceType do
 
     specify { expect(subject.to_h[:schemaExtensions]).to match_array([{ schema: extension, required: false }]) }
   end
+
+  describe '.parse' do
+    let(:extension) { 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' }
+    let(:result) { described_class.parse(subject.to_json) }
+
+    before { subject.add_schema_extension(schema: extension, required: false) }
+
+    specify { expect(result.id).to eql(subject.id) }
+    specify { expect(result.name).to eql(subject.name) }
+    specify { expect(result.description).to eql(subject.description) }
+    specify { expect(result.endpoint).to eql(subject.endpoint) }
+    specify { expect(result.schema).to eql(subject.schema) }
+    specify { expect(result.schema_extensions).to eql(subject.schema_extensions) }
+    specify { expect(result.to_h).to eql(subject.to_h) }
+    specify { expect(result.to_json).to eql(subject.to_json) }
+  end
 end
spec/scim/kit/v2/schema_spec.rb
@@ -122,4 +122,32 @@ RSpec.describe Scim::Kit::V2::Schema do
     specify { expect(result[:meta][:resourceType]).to eql('Schema') }
     specify { expect(result[:meta][:location]).to eql(location) }
   end
+
+  describe '.parse' do
+    let(:result) { described_class.parse(subject.to_json) }
+
+    before do
+      subject.add_attribute(name: :display_name) do |x|
+        x.multi_valued = true
+        x.required = true
+        x.case_exact = true
+        x.mutability = :read_only
+        x.returned = :never
+        x.uniqueness = :server
+        x.canonical_values = ['honerva']
+        x.reference_types = %w[User Group]
+      end
+    end
+
+    specify { expect(result.id).to eql(subject.id) }
+    specify { expect(result.name).to eql(subject.name) }
+    specify { expect(result.description).to eql(subject.description) }
+    specify { expect(result.meta.created).to eql(subject.meta.created) }
+    specify { expect(result.meta.last_modified).to eql(subject.meta.last_modified) }
+    specify { expect(result.meta.version).to eql(subject.meta.version) }
+    specify { expect(result.meta.location).to eql(subject.meta.location) }
+    specify { expect(result.meta.resource_type).to eql(subject.meta.resource_type) }
+    specify { expect(result.to_json).to eql(subject.to_json) }
+    specify { expect(result.to_h).to eql(subject.to_h) }
+  end
 end
spec/scim/kit/v2/service_provider_configuration_spec.rb
@@ -135,4 +135,30 @@ RSpec.describe Scim::Kit::V2::ServiceProviderConfiguration do
       specify { expect(result[:filter][:maxResults]).to be(200) }
     end
   end
+
+  describe '.parse' do
+    let(:result) { described_class.parse(subject.to_json) }
+
+    before do
+      subject.add_authentication(:oauthbearertoken)
+      subject.bulk.max_operations = 1000
+      subject.bulk.max_payload_size = 1_048_576
+      subject.bulk.supported = true
+      subject.change_password.supported = true
+      subject.documentation_uri = FFaker::Internet.uri('https')
+      subject.etag.supported = true
+      subject.filter.max_results = 200
+      subject.filter.supported = true
+      subject.patch.supported = true
+      subject.sort.supported = true
+    end
+
+    specify { expect(result.meta.created.to_i).to eql(subject.meta.created.to_i) }
+    specify { expect(result.meta.last_modified.to_i).to eql(subject.meta.last_modified.to_i) }
+    specify { expect(result.meta.version).to eql(subject.meta.version) }
+    specify { expect(result.meta.location).to eql(subject.meta.location) }
+    specify { expect(result.meta.resource_type).to eql(subject.meta.resource_type) }
+    specify { expect(result.to_json).to eql(subject.to_json) }
+    specify { expect(result.to_h).to eql(subject.to_h) }
+  end
 end
spec/spec_helper.rb
@@ -5,6 +5,7 @@ require 'scim/kit'
 require 'ffaker'
 require 'json'
 require 'byebug'
+require 'webmock/rspec'
 
 Scim::Kit.logger = Logger.new('/dev/null')
 
.rubocop.yml
@@ -20,6 +20,8 @@ Metrics/BlockLength:
 Metrics/LineLength:
   Exclude:
     - 'spec/**/*.rb'
+  IgnoredPatterns:
+    - '^#*'
 
 Naming/FileName:
   Exclude:
.travis.yml
@@ -4,4 +4,5 @@ language: ruby
 cache: bundler
 rvm:
   - 2.5.3
+  - 2.6.1
 before_install: gem install bundler -v 1.17.1
CHANGELOG.md
@@ -1,4 +1,4 @@
-Version 0.2.16
+Version 0.3.0
 # Changelog
 All notable changes to this project will be documented in this file.
 
@@ -6,8 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
-### Changed
-- nil
+- NA
+
+## [0.3.0] - 2019-02-21
+### Added
+- add ServiceProviderConfiguration JSON parsing
+- add Schema JSON parsing
+- add Resource Type JSON parsing
 
 ## [0.2.16] - 2019-02-03
 ### Added
@@ -22,7 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - \_assign does not coerce values by default.
 - errors are merged together instead of overwritten during attribute validation.
 
-[Unreleased]: https://github.com/mokhan/scim-kit/compare/v0.2.16...HEAD
+[Unreleased]: https://github.com/mokhan/scim-kit/compare/v0.3.0...HEAD
+[0.3.0]: https://github.com/mokhan/scim-kit/compare/v0.2.16...v0.3.0
 [0.2.16]: https://github.com/mokhan/scim-kit/compare/v0.2.15...v0.2.16
 [0.2.15]: https://github.com/mokhan/scim-kit/compare/v0.2.14...v0.2.15
 [0.2.14]: https://github.com/mokhan/scim-kit/compare/v0.2.13...v0.2.14
README.md
@@ -1,5 +1,9 @@
 # Scim::Kit
 
+[![Build Status](https://travis-ci.org/mokhan/scim-kit.svg?branch=master)](https://travis-ci.org/mokhan/scim-kit)
+[![Code Climate](https://codeclimate.com/github/mokhan/scim-kit.svg)](https://codeclimate.com/github/mokhan/scim-kit)
+[![Gem Version](https://badge.fury.io/rb/scim-kit.svg)](https://rubygems.org/gems/scim-kit)
+
 Scim::Kit is a library with the purpose of simplifying the generation
 and consumption of SCIM Schema. https://tools.ietf.org/html/rfc7643#section-2
 
@@ -77,13 +81,13 @@ puts user_schema.to_json
 
 ## Development
 
-After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
+After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
 
 To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
 
 ## Contributing
 
-Bug reports and pull requests are welcome on GitHub at https://github.com/mokha/scim-kit.
+Bug reports and pull requests are welcome on GitHub at https://github.com/mokhan/scim-kit.
 
 ## License
 
scim-kit.gemspec
@@ -28,8 +28,10 @@ Gem::Specification.new do |spec|
   end
   spec.require_paths = ['lib']
   spec.required_ruby_version = '>= 2.5.0'
+  spec.metadata['yard.run'] = 'yri'
 
   spec.add_dependency 'activemodel', '>= 5.2.0'
+  spec.add_dependency 'net-hippie', '~> 0.2'
   spec.add_dependency 'tilt', '~> 2.0'
   spec.add_dependency 'tilt-jbuilder', '~> 0.7'
   spec.add_development_dependency 'bundler', '~> 1.17'
@@ -40,4 +42,5 @@ Gem::Specification.new do |spec|
   spec.add_development_dependency 'rspec', '~> 3.0'
   spec.add_development_dependency 'rubocop', '~> 0.52'
   spec.add_development_dependency 'rubocop-rspec', '~> 1.22'
+  spec.add_development_dependency 'webmock', '~> 3.5'
 end