rom-rb / rom-http

Abstract HTTP adapter for ROM
https://rom-rb.org
MIT License
73 stars 18 forks source link

Document creating an adapter more thoroughly #41

Open thedrow opened 8 years ago

thedrow commented 8 years ago

I have the following adapter code:

require 'rom'
require 'rom-http'

module ROM
  module RestfullClientAdapter
    class Dataset < ROM::HTTP::Dataset
      default_request_handler -> (dataset) do
        puts "begin request"
      end

      default_response_handler -> (response, dataset) do
        puts "end request"
      end

      def response_transformer(response)

      end
    end

    class Gateway < ROM::HTTP::Gateway; end

    class Relation < ROM::HTTP::Relation
      adapter :restfull_client

    end

    module Commands
      class Create < ROM::HTTP::Commands::Create
        adapter :restfull_client
      end

      class Update < ROM::HTTP::Commands::Update
        adapter :restfull_client
      end

      class Delete < ROM::HTTP::Commands::Delete
        adapter :restfull_client
      end
    end
  end
end

ROM.register_adapter(:restfull_client, ROM::RestfullClientAdapter)

and I'm getting to following exception:

KeyError: key not found: :base
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-2.0.0/lib/rom/plugins/relation/view.rb:35:in `fetch'
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-2.0.0/lib/rom/plugins/relation/view.rb:35:in `attributes'
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-repository-0.3.1/lib/rom/repository/relation_proxy.rb:212:in `method_missing'
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-repository-0.3.1/lib/rom/repository/relation_proxy.rb:132:in `to_ast'
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-repository-0.3.1/lib/rom/repository.rb:203:in `compile_command'
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-repository-0.3.1/lib/rom/repository.rb:138:in `block in command'
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:133:in `block in fetch_or_store'
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:122:in `fetch'
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:132:in `fetch_or_store'
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-repository-0.3.1/lib/rom/repository.rb:137:in `command'
/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-repository-0.3.1/lib/rom/repository/class_interface.rb:116:in `block in define_command_method'
./spec/rom_restful_client_adapter_spec.rb:15:in `block (2 levels) in <top (required)>'
-e:1:in `load'
-e:1:in `<main>'

After digging a bit it seems I need to set the option base to be an instance but I'm not sure of which class. So I did:

    class Relation < ROM::HTTP::Relation
      adapter :restfull_client

      option :base, self
    end

and now I get:

/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-support-2.0.0/lib/rom/support/options.rb:49:in `initialize': undefined method `fetch' for ROM::RestfullClientAdapter::Relation:Class (NoMethodError)
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-support-2.0.0/lib/rom/support/options.rb:164:in `new'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-support-2.0.0/lib/rom/support/options.rb:164:in `option'
    from /Volumes/fiverr_dev/restfull-client/lib/rom_adapter.rb:25:in `<class:Relation>'
    from /Volumes/fiverr_dev/restfull-client/lib/rom_adapter.rb:22:in `<module:RestfullClientAdapter>'
    from /Volumes/fiverr_dev/restfull-client/lib/rom_adapter.rb:5:in `<module:ROM>'
    from /Volumes/fiverr_dev/restfull-client/lib/rom_adapter.rb:4:in `<top (required)>'
    from /Volumes/fiverr_dev/restfull-client/spec/support/models.rb:2:in `require'
    from /Volumes/fiverr_dev/restfull-client/spec/support/models.rb:2:in `<top (required)>'
    from /Volumes/fiverr_dev/restfull-client/spec/spec_helper.rb:6:in `load'
    from /Volumes/fiverr_dev/restfull-client/spec/spec_helper.rb:6:in `block in <top (required)>'
    from /Volumes/fiverr_dev/restfull-client/spec/spec_helper.rb:6:in `each'
    from /Volumes/fiverr_dev/restfull-client/spec/spec_helper.rb:6:in `<top (required)>'
    from /Users/omer.katz/.rvm/rubies/ruby-2.2.2/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
    from /Users/omer.katz/.rvm/rubies/ruby-2.2.2/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration.rb:1394:in `block in requires='
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration.rb:1394:in `each'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration.rb:1394:in `requires='
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration_options.rb:112:in `block in process_options_into'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration_options.rb:111:in `each'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration_options.rb:111:in `process_options_into'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration_options.rb:21:in `configure'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:99:in `setup'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:86:in `run'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/invocations.rb:23:in `call'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:69:in `run'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:45:in `invoke'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/exe/rspec:4:in `<top (required)>'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/bin/rspec:23:in `load'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/bin/rspec:23:in `<top (required)>'
    from -e:1:in `load'
    from -e:1:in `<main>'

After adding fetch to the Relation class I get:

/Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-2.0.0/lib/rom/relation/class_interface.rb:129:in `rescue in []': Failed to find relation class for coercer adapter. Make sure ROM setup was started and the adapter identifier is correct. (ROM::AdapterNotPresentError)
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-2.0.0/lib/rom/relation/class_interface.rb:127:in `[]'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-support-2.0.0/lib/rom/support/options.rb:51:in `initialize'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-support-2.0.0/lib/rom/support/options.rb:164:in `new'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rom-support-2.0.0/lib/rom/support/options.rb:164:in `option'
    from /Volumes/fiverr_dev/restfull-client/lib/rom_adapter.rb:29:in `<class:Relation>'
    from /Volumes/fiverr_dev/restfull-client/lib/rom_adapter.rb:22:in `<module:RestfullClientAdapter>'
    from /Volumes/fiverr_dev/restfull-client/lib/rom_adapter.rb:5:in `<module:ROM>'
    from /Volumes/fiverr_dev/restfull-client/lib/rom_adapter.rb:4:in `<top (required)>'
    from /Volumes/fiverr_dev/restfull-client/spec/support/models.rb:2:in `require'
    from /Volumes/fiverr_dev/restfull-client/spec/support/models.rb:2:in `<top (required)>'
    from /Volumes/fiverr_dev/restfull-client/spec/spec_helper.rb:6:in `load'
    from /Volumes/fiverr_dev/restfull-client/spec/spec_helper.rb:6:in `block in <top (required)>'
    from /Volumes/fiverr_dev/restfull-client/spec/spec_helper.rb:6:in `each'
    from /Volumes/fiverr_dev/restfull-client/spec/spec_helper.rb:6:in `<top (required)>'
    from /Users/omer.katz/.rvm/rubies/ruby-2.2.2/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
    from /Users/omer.katz/.rvm/rubies/ruby-2.2.2/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration.rb:1394:in `block in requires='
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration.rb:1394:in `each'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration.rb:1394:in `requires='
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration_options.rb:112:in `block in process_options_into'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration_options.rb:111:in `each'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration_options.rb:111:in `process_options_into'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/configuration_options.rb:21:in `configure'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:99:in `setup'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:86:in `run'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/invocations.rb:23:in `call'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:69:in `run'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/lib/rspec/core/runner.rb:45:in `invoke'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/gems/rspec-core-3.5.2/exe/rspec:4:in `<top (required)>'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/bin/rspec:23:in `load'
    from /Users/omer.katz/.rvm/gems/ruby-2.2.2@restfull_client/bin/rspec:23:in `<top (required)>'
    from -e:1:in `load'
    from -e:1:in `<main>'

It's really not clear what I should be doing. Can you guys please help me and also document the process?

AMHOL commented 8 years ago

Sorry, just looked into this and it seems that the dataset no longer infers register_as, you don't need a base view unless you're using rom-repository, try updating your relation to use register_as:

class Users < ROM::Relation[:my_adapter]
  dataset :users
  register_as :users

  def by_id(id)
    with_path(id.to_s)
  end
end

Full example:

require 'json'
require 'uri'
require 'net/http'

gemfile(true) do
  gem 'rom'
  gem 'rom-http'
end

module ROM
  module MyAdapter
    class Dataset < ROM::HTTP::Dataset
      default_request_handler ->(dataset) do
        uri = URI(dataset.uri)
        uri.path = "/#{dataset.name}/#{dataset.path}"
        uri.query = URI.encode_www_form(dataset.params)

        http = Net::HTTP.new(uri.host, uri.port)
        request_klass = Net::HTTP.const_get(ROM::Inflector.classify(dataset.request_method))

        request = request_klass.new(uri.request_uri)
        dataset.headers.each_with_object(request) do |(header, value), request|
          request[header.to_s] = value
        end

        response = http.request(request)
      end

      default_response_handler ->(response, dataset) do
        Array([JSON.parse(response.body)]).flatten
      end
    end

    class Gateway < ROM::HTTP::Gateway; end

    class Relation < ROM::HTTP::Relation
      adapter :my_adapter
    end

    module Commands
      class Create < ROM::HTTP::Commands::Create
        adapter :my_adapter
      end

      class Update < ROM::HTTP::Commands::Update
        adapter :my_adapter
      end

      class Delete < ROM::HTTP::Commands::Delete
        adapter :my_adapter
      end
    end
  end
end

ROM.register_adapter(:my_adapter, ROM::MyAdapter)

configuration = ROM::Configuration.new(:my_adapter, {
  uri: 'http://jsonplaceholder.typicode.com',
  headers: {
    Accept: 'application/json'
  }
})

class Users < ROM::Relation[:my_adapter]
  dataset :users
  register_as :users

  def by_id(id)
    with_path(id.to_s)
  end
end

configuration.register_relation(Users)
container = ROM.container(configuration)

container.relation(:users).by_id(1).to_a
thedrow commented 8 years ago

I am using rom-repository.

AMHOL commented 8 years ago

Here's a working example with rom-repository:

require 'json'
require 'uri'
require 'net/http'

gemfile(true) do
  gem 'rom'
  gem 'rom-http'
  gem 'rom-repository'
end

module ROM
  module MyAdapter
    class Dataset < ROM::HTTP::Dataset
      default_request_handler ->(dataset) do
        uri = URI(dataset.uri)
        uri.path = "/#{dataset.name}/#{dataset.path}"
        uri.query = URI.encode_www_form(dataset.params)

        http = Net::HTTP.new(uri.host, uri.port)
        request_klass = Net::HTTP.const_get(ROM::Inflector.classify(dataset.request_method))

        request = request_klass.new(uri.request_uri)
        dataset.headers.each_with_object(request) do |(header, value), request|
          request[header.to_s] = value
        end

        response = http.request(request)
      end

      default_response_handler ->(response, dataset) do
        Array([JSON.parse(response.body, symbolize_names: true)]).flatten
      end
    end

    class Gateway < ROM::HTTP::Gateway; end

    class Relation < ROM::HTTP::Relation
      adapter :my_adapter
    end

    module Commands
      class Create < ROM::HTTP::Commands::Create
        adapter :my_adapter
      end

      class Update < ROM::HTTP::Commands::Update
        adapter :my_adapter
      end

      class Delete < ROM::HTTP::Commands::Delete
        adapter :my_adapter
      end
    end
  end
end

ROM.register_adapter(:my_adapter, ROM::MyAdapter)

configuration = ROM::Configuration.new(:my_adapter, {
  uri: 'http://jsonplaceholder.typicode.com',
  headers: {
    Accept: 'application/json'
  }
})

class Users < ROM::Relation[:my_adapter]
  dataset :users
  register_as :users

  schema do
    attribute :id, ROM::Types::Int
    attribute :name, ROM::Types::String
    attribute :username, ROM::Types::String
    attribute :email, ROM::Types::String
    attribute :phone, ROM::Types::String
    attribute :website, ROM::Types::String
  end

  view(:base, %i(id name username email phone website)) do
    self
  end

  def by_id(id)
    with_path(id.to_s)
  end
end

configuration.register_relation(Users)
container = ROM.container(configuration)

class UserRepository < ROM::Repository[:users]
  def find(id)
    users.by_id(id).one!
  end
end

user_repo = UserRepository.new(container)
user_repo.find(1)
# => #<ROM::Struct[User] id=1 name="Leanne Graham" username="Bret" email="Sincere@april.biz" phone="1-770-736-8031 x56442" website="hildegard.org">
AMHOL commented 7 years ago

Closing due to inactivity

thedrow commented 7 years ago

Why are you closing this issue? Has the documentation been updated and extended?

AMHOL commented 7 years ago

Fair point