jimson 0.1.0
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.
- data/CHANGELOG.rdoc +0 -0
- data/LICENSE.txt +17 -0
- data/README.rdoc +0 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/lib/jimson.rb +11 -0
- data/lib/jimson/client.rb +163 -0
- data/lib/jimson/client_error.rb +28 -0
- data/lib/jimson/request.rb +25 -0
- data/lib/jimson/response.rb +30 -0
- data/lib/jimson/server.rb +158 -0
- data/lib/jimson/server_error.rb +58 -0
- data/spec/client_spec.rb +80 -0
- data/spec/em_helper.rb +34 -0
- data/spec/server_spec.rb +202 -0
- data/spec/spec_helper.rb +25 -0
- metadata +117 -0
    
        data/CHANGELOG.rdoc
    ADDED
    
    | 
            File without changes
         | 
    
        data/LICENSE.txt
    ADDED
    
    | @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
| 2 | 
            +
            of this software and associated documentation files (the "Software"), to deal
         | 
| 3 | 
            +
            in the Software without restriction, including without limitation the rights
         | 
| 4 | 
            +
            to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         | 
| 5 | 
            +
            copies of the Software, and to permit persons to whom the Software is
         | 
| 6 | 
            +
            furnished to do so, subject to the following conditions:
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            The above copyright notice and this permission notice shall be included in
         | 
| 9 | 
            +
            all copies or substantial portions of the Software.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         | 
| 12 | 
            +
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         | 
| 13 | 
            +
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         | 
| 14 | 
            +
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         | 
| 15 | 
            +
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         | 
| 16 | 
            +
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         | 
| 17 | 
            +
            THE SOFTWARE.
         | 
    
        data/README.rdoc
    ADDED
    
    | 
            File without changes
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            require 'rubygems'
         | 
| 2 | 
            +
            require 'rake'
         | 
| 3 | 
            +
            require 'rspec/core/rake_task'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            desc "Run all specs"
         | 
| 6 | 
            +
            RSpec::Core::RakeTask.new(:rspec) do |spec|
         | 
| 7 | 
            +
              spec.pattern = 'spec/**/*_spec.rb'
         | 
| 8 | 
            +
            end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            RSpec::Core::RakeTask.new(:rcov) do |spec|
         | 
| 11 | 
            +
              spec.pattern = 'spec/**/*_spec.rb'
         | 
| 12 | 
            +
              spec.rcov = true
         | 
| 13 | 
            +
            end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            task :default => :rspec
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            require 'rake/rdoctask'
         | 
| 18 | 
            +
            Rake::RDocTask.new do |rdoc|
         | 
| 19 | 
            +
              version = File.exist?('VERSION') ? File.read('VERSION') : ""
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              rdoc.rdoc_dir = 'rdoc'
         | 
| 22 | 
            +
              rdoc.title = "jimson #{version}"
         | 
| 23 | 
            +
              rdoc.rdoc_files.include('README*')
         | 
| 24 | 
            +
              rdoc.rdoc_files.include('lib/**/*.rb')
         | 
| 25 | 
            +
            end
         | 
    
        data/VERSION
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            0.1.0
         | 
    
        data/lib/jimson.rb
    ADDED
    
    
| @@ -0,0 +1,163 @@ | |
| 1 | 
            +
            require 'patron'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Jimson
         | 
| 4 | 
            +
              class ClientHelper
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                def self.make_id
         | 
| 7 | 
            +
                  rand(10**12)
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def initialize(url)
         | 
| 11 | 
            +
                  @http = Patron::Session.new
         | 
| 12 | 
            +
                  uri = URI(url)
         | 
| 13 | 
            +
                  @path = uri.path
         | 
| 14 | 
            +
                  @http.base_url = "#{uri.scheme}://#{uri.host}:#{uri.port}"
         | 
| 15 | 
            +
                  @batch = []
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def process_call(sym, args)
         | 
| 19 | 
            +
                  resp = send_single_request(sym.to_s, args)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  begin
         | 
| 22 | 
            +
                    data = JSON.parse(resp)
         | 
| 23 | 
            +
                  rescue
         | 
| 24 | 
            +
                    raise Jimson::ClientError::InvalidJSON.new(json)
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  return process_single_response(data)
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def send_single_request(method, args)
         | 
| 31 | 
            +
                  post_data = {
         | 
| 32 | 
            +
                                'jsonrpc' => '2.0',
         | 
| 33 | 
            +
                                'method'  => method,
         | 
| 34 | 
            +
                                'params'  => args,
         | 
| 35 | 
            +
                                'id'      => self.class.make_id
         | 
| 36 | 
            +
                              }.to_json
         | 
| 37 | 
            +
                  resp = @http.post(@path, post_data)
         | 
| 38 | 
            +
                  if resp.nil? || resp.body.nil? || resp.body.empty?
         | 
| 39 | 
            +
                    raise Jimson::ClientError::InvalidResponse.new
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  return resp.body
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  rescue Exception, StandardError
         | 
| 45 | 
            +
                    raise new Jimson::ClientError::InternalError.new($!)
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def send_batch_request(batch)
         | 
| 49 | 
            +
                  post_data = batch.to_json
         | 
| 50 | 
            +
                  resp = @http.post(@path, post_data)
         | 
| 51 | 
            +
                  if resp.nil? || resp.body.nil? || resp.body.empty?
         | 
| 52 | 
            +
                    raise Jimson::ClientError::InvalidResponse.new
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  return resp.body
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def process_batch_response(responses)
         | 
| 59 | 
            +
                  responses.each do |resp|
         | 
| 60 | 
            +
                    saved_response = @batch.map { |r| r[1] }.select { |r| r.id == resp['id'] }.first
         | 
| 61 | 
            +
                    raise Jimson::ClientError::InvalidResponse.new unless !!saved_response
         | 
| 62 | 
            +
                    saved_response.populate!(resp)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def process_single_response(data)
         | 
| 67 | 
            +
                  raise Jimson::ClientError::InvalidResponse.new if !valid_response?(data)
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  if !!data['error']
         | 
| 70 | 
            +
                    code = data['error']['code']
         | 
| 71 | 
            +
                    if Jimson::ServerError::CODES.keys.include?(code)
         | 
| 72 | 
            +
                      raise Jimson::ServerError::CODES[code].new
         | 
| 73 | 
            +
                    else
         | 
| 74 | 
            +
                      raise Jimson::ClientError::UnknownServerError.new(code, data['error']['message'])
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  return data['result']
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  rescue Exception, StandardError
         | 
| 81 | 
            +
                    raise new Jimson::ClientError::InternalError.new
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                def valid_response?(data)
         | 
| 85 | 
            +
                  return false if !data.is_a?(Hash)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  return false if data['jsonrpc'] != '2.0'
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  return false if !data.has_key?('id')
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  return false if data.has_key?('error') && data.has_key?('result')
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  if data.has_key?('error')
         | 
| 94 | 
            +
                    if !data['error'].is_a?(Hash) || !data['error'].has_key?('code') || !data['error'].has_key?('message') 
         | 
| 95 | 
            +
                      return false
         | 
| 96 | 
            +
                    end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                    if !data['error']['code'].is_a?(Fixnum) || !data['error']['message'].is_a?(String)
         | 
| 99 | 
            +
                      return false
         | 
| 100 | 
            +
                    end
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                  return true
         | 
| 104 | 
            +
                  
         | 
| 105 | 
            +
                  rescue
         | 
| 106 | 
            +
                    return false
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                def push_batch_request(request)
         | 
| 110 | 
            +
                  request.id = self.class.make_id
         | 
| 111 | 
            +
                  response = Jimson::Response.new(request.id)
         | 
| 112 | 
            +
                  @batch << [request, response]
         | 
| 113 | 
            +
                  return response
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                def send_batch
         | 
| 117 | 
            +
                  batch = @batch.map(&:first) # get the requests 
         | 
| 118 | 
            +
                  response = send_batch_request(batch)
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                  begin
         | 
| 121 | 
            +
                    responses = JSON.parse(response)
         | 
| 122 | 
            +
                  rescue
         | 
| 123 | 
            +
                    raise Jimson::ClientError::InvalidJSON.new(json)
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  process_batch_response(responses)
         | 
| 127 | 
            +
                  @batch = []
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
              end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
              class BatchClient
         | 
| 133 | 
            +
                
         | 
| 134 | 
            +
                def initialize(helper)
         | 
| 135 | 
            +
                  @helper = helper
         | 
| 136 | 
            +
                end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                def method_missing(sym, *args, &block)
         | 
| 139 | 
            +
                  request = Jimson::Request.new(sym.to_s, args)
         | 
| 140 | 
            +
                  @helper.push_batch_request(request) 
         | 
| 141 | 
            +
                end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
              end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
              class Client
         | 
| 146 | 
            +
                
         | 
| 147 | 
            +
                def self.batch(client)
         | 
| 148 | 
            +
                  helper = client.instance_variable_get(:@helper)
         | 
| 149 | 
            +
                  batch_client = BatchClient.new(helper)
         | 
| 150 | 
            +
                  yield batch_client
         | 
| 151 | 
            +
                  helper.send_batch
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                def initialize(url)
         | 
| 155 | 
            +
                  @helper = ClientHelper.new(url)
         | 
| 156 | 
            +
                end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                def method_missing(sym, *args, &block)
         | 
| 159 | 
            +
                  @helper.process_call(sym, args) 
         | 
| 160 | 
            +
                end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
              end
         | 
| 163 | 
            +
            end
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            module Jimson
         | 
| 2 | 
            +
              module ClientError
         | 
| 3 | 
            +
                class InvalidResponse < Exception
         | 
| 4 | 
            +
                  def initialize()
         | 
| 5 | 
            +
                    super('Invalid or empty response from server.')
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                class InvalidJSON < Exception
         | 
| 10 | 
            +
                  def initialize(json)
         | 
| 11 | 
            +
                    super("Couldn't parse JSON string received from server:\n#{json}")
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                class InternalError < Exception
         | 
| 16 | 
            +
                  def initialize(e)
         | 
| 17 | 
            +
                    super("An internal client error occurred when processing the request: #{e}\n#{e.backtrace.join("\n")}")
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                class UnknownServerError < Exception
         | 
| 22 | 
            +
                  def initialize(code, message)
         | 
| 23 | 
            +
                    super("The server specified an error the client doesn't know about: #{code} #{message}")
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            module Jimson
         | 
| 2 | 
            +
              class Request
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                attr_accessor :method, :params, :id
         | 
| 5 | 
            +
                def initialize(method, params, id = nil)
         | 
| 6 | 
            +
                  @method = method
         | 
| 7 | 
            +
                  @params = params
         | 
| 8 | 
            +
                  @id = id
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def to_h
         | 
| 12 | 
            +
                  h = {
         | 
| 13 | 
            +
                    'jsonrpc' => '2.0',
         | 
| 14 | 
            +
                    'method'  => @method
         | 
| 15 | 
            +
                  }
         | 
| 16 | 
            +
                  h.merge!('params' => @params) if !!@params && !params.empty?
         | 
| 17 | 
            +
                  h.merge!('id' => id)
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def to_json(*a)
         | 
| 21 | 
            +
                  self.to_h.to_json(*a)
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module Jimson
         | 
| 2 | 
            +
              class Response
         | 
| 3 | 
            +
                attr_accessor :result, :error, :id
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(id)
         | 
| 6 | 
            +
                  @id = id
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def to_h
         | 
| 10 | 
            +
                  h = {'jsonrpc' => '2.0'}
         | 
| 11 | 
            +
                  h.merge!('result' => @result) if !!@result
         | 
| 12 | 
            +
                  h.merge!('error' => @error) if !!@error
         | 
| 13 | 
            +
                  h.merge!('id' => @id)
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def is_error?
         | 
| 17 | 
            +
                  !!@error
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def succeeded?
         | 
| 21 | 
            +
                  !!@result
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def populate!(data)
         | 
| 25 | 
            +
                  @error = data['error'] if !!data['error']
         | 
| 26 | 
            +
                  @result = data['result'] if !!data['result']
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -0,0 +1,158 @@ | |
| 1 | 
            +
            require 'eventmachine'
         | 
| 2 | 
            +
            require 'evma_httpserver'
         | 
| 3 | 
            +
            require 'logger'
         | 
| 4 | 
            +
            require 'json'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Jimson
         | 
| 7 | 
            +
              class HttpServer < EM::Connection
         | 
| 8 | 
            +
                include EM::HttpServer
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def self.handler=(handler)
         | 
| 11 | 
            +
                  @@handler = handler
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def process_http_request
         | 
| 15 | 
            +
                  resp = EM::DelegatedHttpResponse.new( self )
         | 
| 16 | 
            +
                  resp.status = 200
         | 
| 17 | 
            +
                  resp.content = process_post(@http_post_content)
         | 
| 18 | 
            +
                  resp.send_response
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def process_post(content)
         | 
| 22 | 
            +
                  begin
         | 
| 23 | 
            +
                    request = parse_request(@http_post_content)
         | 
| 24 | 
            +
                    if request.is_a?(Array)
         | 
| 25 | 
            +
                      raise Jimson::ServerError::InvalidRequest.new if request.empty?
         | 
| 26 | 
            +
                      response = request.map { |req| handle_request(req) }
         | 
| 27 | 
            +
                    else
         | 
| 28 | 
            +
                      response = handle_request(request)
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  rescue Jimson::ServerError::ParseError, Jimson::ServerError::InvalidRequest => e
         | 
| 31 | 
            +
                    response = error_response(e)
         | 
| 32 | 
            +
                  rescue Jimson::ServerError::Generic => e
         | 
| 33 | 
            +
                    response = error_response(e, request)
         | 
| 34 | 
            +
                  rescue StandardError, Exception
         | 
| 35 | 
            +
                    response = error_response(Jimson::ServerError::InternalError.new)
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  response.compact! if response.is_a?(Array)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  return nil if response.nil? || (response.respond_to?(:empty?) && response.empty?)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  response.to_json
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def handle_request(request)
         | 
| 46 | 
            +
                  response = nil
         | 
| 47 | 
            +
                  begin
         | 
| 48 | 
            +
                    if !validate_request(request)
         | 
| 49 | 
            +
                      response = error_response(Jimson::ServerError::InvalidRequest.new)
         | 
| 50 | 
            +
                    else
         | 
| 51 | 
            +
                      response = create_response(request)
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
                  rescue Jimson::ServerError::Generic => e
         | 
| 54 | 
            +
                    response = error_response(e, request)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  response
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def validate_request(request)
         | 
| 61 | 
            +
                  required_keys = %w(jsonrpc method)
         | 
| 62 | 
            +
                  required_types = {
         | 
| 63 | 
            +
                                     'jsonrpc' => [String],
         | 
| 64 | 
            +
                                     'method'  => [String], 
         | 
| 65 | 
            +
                                     'params'  => [Hash, Array],
         | 
| 66 | 
            +
                                     'id'      => [String, Fixnum, NilClass]
         | 
| 67 | 
            +
                                   }
         | 
| 68 | 
            +
                  
         | 
| 69 | 
            +
                  return false if !request.is_a?(Hash)
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  required_keys.each do |key|
         | 
| 72 | 
            +
                    return false if !request.has_key?(key)
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  required_types.each do |key, types|
         | 
| 76 | 
            +
                    return false if request.has_key?(key) && !types.any? { |type| request[key].is_a?(type) }
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  return false if request['jsonrpc'] != '2.0'
         | 
| 80 | 
            +
                  
         | 
| 81 | 
            +
                  true
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                def create_response(request)
         | 
| 85 | 
            +
                  params = request['params']
         | 
| 86 | 
            +
                  begin
         | 
| 87 | 
            +
                    if params.is_a?(Hash)
         | 
| 88 | 
            +
                      result = @@handler.send(request['method'], params)
         | 
| 89 | 
            +
                    else
         | 
| 90 | 
            +
                      result = @@handler.send(request['method'], *params)
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
                  rescue NoMethodError
         | 
| 93 | 
            +
                    raise Jimson::ServerError::MethodNotFound.new 
         | 
| 94 | 
            +
                  rescue ArgumentError
         | 
| 95 | 
            +
                    raise Jimson::ServerError::InvalidParams.new
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  response = success_response(request, result)
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  # A Notification is a Request object without an "id" member.
         | 
| 101 | 
            +
                  # The Server MUST NOT reply to a Notification, including those 
         | 
| 102 | 
            +
                  # that are within a batch request.
         | 
| 103 | 
            +
                  response = nil if !request.has_key?('id')
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  response 
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                def error_response(error, request = nil)
         | 
| 109 | 
            +
                  resp = {
         | 
| 110 | 
            +
                           'jsonrpc' => JSON_RPC_VERSION,
         | 
| 111 | 
            +
                           'error'   => error.to_h,
         | 
| 112 | 
            +
                         }
         | 
| 113 | 
            +
                  if !!request && request.has_key?('id')
         | 
| 114 | 
            +
                    resp['id'] = request['id'] 
         | 
| 115 | 
            +
                  else
         | 
| 116 | 
            +
                    resp['id'] = nil
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                  resp
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                def success_response(request, result)
         | 
| 123 | 
            +
                  {
         | 
| 124 | 
            +
                    'jsonrpc' => JSON_RPC_VERSION,
         | 
| 125 | 
            +
                    'result'  => result,  
         | 
| 126 | 
            +
                    'id'      => request['id']
         | 
| 127 | 
            +
                  }
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                def parse_request(post)
         | 
| 131 | 
            +
                  data = JSON.parse(post)
         | 
| 132 | 
            +
                  rescue 
         | 
| 133 | 
            +
                    raise Jimson::ServerError::ParseError.new 
         | 
| 134 | 
            +
                end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
              end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
              class Server
         | 
| 139 | 
            +
                
         | 
| 140 | 
            +
                attr_accessor :handler, :host, :port, :logger
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                def initialize(handler, host = '0.0.0.0', port = 8999, logger = Logger.new(STDOUT))
         | 
| 143 | 
            +
                  @handler = handler
         | 
| 144 | 
            +
                  @host = host
         | 
| 145 | 
            +
                  @port = port
         | 
| 146 | 
            +
                  @logger = logger
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                def start
         | 
| 150 | 
            +
                  Jimson::HttpServer.handler = @handler
         | 
| 151 | 
            +
                  EM.run do
         | 
| 152 | 
            +
                    EM::start_server(@host, @port, Jimson::HttpServer)
         | 
| 153 | 
            +
                    @logger.info("Server listening on #{@host}:#{@port} with handler '#{@handler}'")
         | 
| 154 | 
            +
                  end
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
              end
         | 
| 158 | 
            +
            end
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            module Jimson
         | 
| 2 | 
            +
              module ServerError
         | 
| 3 | 
            +
                class Generic < Exception
         | 
| 4 | 
            +
                  attr_accessor :code, :message
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize(code, message)
         | 
| 7 | 
            +
                    @code = code
         | 
| 8 | 
            +
                    @message = message
         | 
| 9 | 
            +
                    super(message)
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def to_h
         | 
| 13 | 
            +
                    {
         | 
| 14 | 
            +
                      'code'    => @code,
         | 
| 15 | 
            +
                      'message' => @message
         | 
| 16 | 
            +
                    }
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                class ParseError < Generic
         | 
| 21 | 
            +
                  def initialize
         | 
| 22 | 
            +
                    super(-32700, 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.')
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                class InvalidRequest < Generic
         | 
| 27 | 
            +
                  def initialize
         | 
| 28 | 
            +
                    super(-32600, 'The JSON sent is not a valid Request object.')
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                class MethodNotFound < Generic
         | 
| 33 | 
            +
                  def initialize
         | 
| 34 | 
            +
                    super(-32601, 'Method not found.')
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                class InvalidParams < Generic
         | 
| 39 | 
            +
                  def initialize
         | 
| 40 | 
            +
                    super(-32602, 'Invalid method parameter(s).')
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                class InternalError < Generic
         | 
| 45 | 
            +
                  def initialize
         | 
| 46 | 
            +
                    super(-32603, 'Internal server error.')
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                CODES = {
         | 
| 51 | 
            +
                          -32700 => ParseError,
         | 
| 52 | 
            +
                          -32600 => InvalidRequest,
         | 
| 53 | 
            +
                          -32601 => MethodNotFound,
         | 
| 54 | 
            +
                          -32602 => InvalidParams,
         | 
| 55 | 
            +
                          -32603 => InternalError
         | 
| 56 | 
            +
                        }
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
            end
         | 
    
        data/spec/client_spec.rb
    ADDED
    
    | @@ -0,0 +1,80 @@ | |
| 1 | 
            +
            module Jimson
         | 
| 2 | 
            +
              describe Client do
         | 
| 3 | 
            +
                BOILERPLATE = {'jsonrpc' => '2.0', 'id' => 1}
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                before(:each) do
         | 
| 6 | 
            +
                  @http_mock = mock('http')
         | 
| 7 | 
            +
                  Patron::Session.stub!(:new).and_return(@http_mock)
         | 
| 8 | 
            +
                  @http_mock.should_receive(:base_url=).with(SPEC_URL)
         | 
| 9 | 
            +
                  @resp_mock = mock('http_response')
         | 
| 10 | 
            +
                  ClientHelper.stub!(:make_id).and_return(1)
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                after(:each) do
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                describe "sending a single request" do
         | 
| 17 | 
            +
                  context "when using positional parameters" do
         | 
| 18 | 
            +
                    before(:each) do
         | 
| 19 | 
            +
                      @expected = {
         | 
| 20 | 
            +
                                   'jsonrpc' => '2.0',
         | 
| 21 | 
            +
                                   'method'  => 'foo',
         | 
| 22 | 
            +
                                   'params'  => [1,2,3],
         | 
| 23 | 
            +
                                   'id'      => 1
         | 
| 24 | 
            +
                                 }.to_json
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                    it "sends a valid JSON-RPC request and returns the result" do
         | 
| 27 | 
            +
                      response = BOILERPLATE.merge({'result' => 42}).to_json
         | 
| 28 | 
            +
                      @http_mock.should_receive(:post).with('', @expected).and_return(@resp_mock)
         | 
| 29 | 
            +
                      @resp_mock.should_receive(:body).at_least(:once).and_return(response)
         | 
| 30 | 
            +
                      client = Client.new(SPEC_URL)
         | 
| 31 | 
            +
                      client.foo(1,2,3).should == 42
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                describe "sending a batch request" do
         | 
| 37 | 
            +
                  it "sends a valid JSON-RPC batch request and puts the results in the response objects" do
         | 
| 38 | 
            +
                    batch = [
         | 
| 39 | 
            +
                        {"jsonrpc" => "2.0", "method" => "sum", "params" => [1,2,4], "id" => "1"},
         | 
| 40 | 
            +
                        {"jsonrpc" => "2.0", "method" => "subtract", "params" => [42,23], "id" => "2"},
         | 
| 41 | 
            +
                        {"jsonrpc" => "2.0", "method" => "foo_get", "params" => [{"name" => "myself"}], "id" => "5"},
         | 
| 42 | 
            +
                        {"jsonrpc" => "2.0", "method" => "get_data", "id" => "9"} 
         | 
| 43 | 
            +
                    ].to_json
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    response = [
         | 
| 46 | 
            +
                        {"jsonrpc" => "2.0", "result" => 7, "id" => "1"},
         | 
| 47 | 
            +
                        {"jsonrpc" => "2.0", "result" => 19, "id" => "2"},
         | 
| 48 | 
            +
                        {"jsonrpc" => "2.0", "error" => {"code" => -32601, "message" => "Method not found."}, "id" => "5"},
         | 
| 49 | 
            +
                        {"jsonrpc" => "2.0", "result" => ["hello", 5], "id" => "9"}
         | 
| 50 | 
            +
                    ].to_json
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    ClientHelper.stub!(:make_id).and_return('1', '2', '5', '9')
         | 
| 53 | 
            +
                    @http_mock.should_receive(:post).with('', batch).and_return(@resp_mock)
         | 
| 54 | 
            +
                    @resp_mock.should_receive(:body).at_least(:once).and_return(response)
         | 
| 55 | 
            +
                    client = Client.new(SPEC_URL)
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    sum = subtract = foo = data = nil
         | 
| 58 | 
            +
                    Jimson::Client.batch(client) do |batch|
         | 
| 59 | 
            +
                      sum = batch.sum(1,2,4)
         | 
| 60 | 
            +
                      subtract = batch.subtract(42,23)
         | 
| 61 | 
            +
                      foo = batch.foo_get('name' => 'myself')
         | 
| 62 | 
            +
                      data = batch.get_data
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    sum.succeeded?.should be_true
         | 
| 66 | 
            +
                    sum.is_error?.should be_false
         | 
| 67 | 
            +
                    sum.result.should == 7
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    subtract.result.should == 19
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    foo.is_error?.should be_true
         | 
| 72 | 
            +
                    foo.succeeded?.should be_false
         | 
| 73 | 
            +
                    foo.error['code'].should == -32601
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    data.result.should == ['hello', 5]
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
            end
         | 
    
        data/spec/em_helper.rb
    ADDED
    
    | @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'rubygems'
         | 
| 4 | 
            +
            $:.unshift(File.dirname(__FILE__) + '/../lib/')
         | 
| 5 | 
            +
            require 'jimson'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            class TestHandler
         | 
| 8 | 
            +
              def subtract(a, b = nil)
         | 
| 9 | 
            +
                if a.is_a?(Hash)
         | 
| 10 | 
            +
                  return a['minuend'] - a['subtrahend']
         | 
| 11 | 
            +
                else 
         | 
| 12 | 
            +
                  return a - b
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              def sum(a,b,c)
         | 
| 17 | 
            +
                a + b + c
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              def notify_hello(*args)
         | 
| 21 | 
            +
                # notification, doesn't do anything
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def update(*args)
         | 
| 25 | 
            +
                # notification, doesn't do anything
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              def get_data
         | 
| 29 | 
            +
                ['hello', 5]
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            server = Jimson::Server.new(TestHandler.new)
         | 
| 34 | 
            +
            server.start
         | 
    
        data/spec/server_spec.rb
    ADDED
    
    | @@ -0,0 +1,202 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Jimson
         | 
| 4 | 
            +
                describe Server do
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  INVALID_RESPONSE_EXPECTATION = {
         | 
| 7 | 
            +
                                                    'jsonrpc' => '2.0',
         | 
| 8 | 
            +
                                                    'error'   => {
         | 
| 9 | 
            +
                                                                    'code' => -32600,
         | 
| 10 | 
            +
                                                                    'message' => 'The JSON sent is not a valid Request object.'
         | 
| 11 | 
            +
                                                                  },
         | 
| 12 | 
            +
                                                    'id'      => nil
         | 
| 13 | 
            +
                                                  }
         | 
| 14 | 
            +
                  before(:each) do
         | 
| 15 | 
            +
                    @sess = Patron::Session.new
         | 
| 16 | 
            +
                    @sess.base_url = SPEC_URL
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  describe "receiving a request with positional parameters" do
         | 
| 20 | 
            +
                    context "when no errors occur" do
         | 
| 21 | 
            +
                      it "returns a response with 'result'" do
         | 
| 22 | 
            +
                        req = {
         | 
| 23 | 
            +
                                'jsonrpc' => '2.0',
         | 
| 24 | 
            +
                                'method'  => 'subtract',
         | 
| 25 | 
            +
                                'params'  => [24, 20],
         | 
| 26 | 
            +
                                'id'      => 1
         | 
| 27 | 
            +
                              }
         | 
| 28 | 
            +
                        resp = JSON.parse(@sess.post('/', req.to_json).body)
         | 
| 29 | 
            +
                        resp.should == {
         | 
| 30 | 
            +
                                         'jsonrpc' => '2.0',
         | 
| 31 | 
            +
                                         'result'  => 4,
         | 
| 32 | 
            +
                                         'id'      => 1
         | 
| 33 | 
            +
                                       }
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  describe "receiving a request with named parameters" do
         | 
| 39 | 
            +
                    context "when no errors occur" do
         | 
| 40 | 
            +
                      it "returns a response with 'result'" do
         | 
| 41 | 
            +
                        req = {
         | 
| 42 | 
            +
                                'jsonrpc' => '2.0',
         | 
| 43 | 
            +
                                'method'  => 'subtract',
         | 
| 44 | 
            +
                                'params'  => {'subtrahend'=> 20, 'minuend' => 24},
         | 
| 45 | 
            +
                                'id'      => 1
         | 
| 46 | 
            +
                              }
         | 
| 47 | 
            +
                        resp = JSON.parse(@sess.post('/', req.to_json).body)
         | 
| 48 | 
            +
                        resp.should == {
         | 
| 49 | 
            +
                                         'jsonrpc' => '2.0',
         | 
| 50 | 
            +
                                         'result'  => 4,
         | 
| 51 | 
            +
                                         'id'      => 1
         | 
| 52 | 
            +
                                       }
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  describe "receiving a notification" do
         | 
| 58 | 
            +
                    context "when no errors occur" do
         | 
| 59 | 
            +
                      it "returns no response" do
         | 
| 60 | 
            +
                        req = {
         | 
| 61 | 
            +
                                'jsonrpc' => '2.0',
         | 
| 62 | 
            +
                                'method'  => 'update',
         | 
| 63 | 
            +
                                'params'  => [1,2,3,4,5]
         | 
| 64 | 
            +
                              }
         | 
| 65 | 
            +
                        resp = @sess.post('/', req.to_json).body
         | 
| 66 | 
            +
                        resp.should be_empty
         | 
| 67 | 
            +
                      end
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  describe "receiving a call for a non-existent method" do
         | 
| 72 | 
            +
                    it "returns an error response" do
         | 
| 73 | 
            +
                      req = {
         | 
| 74 | 
            +
                              'jsonrpc' => '2.0',
         | 
| 75 | 
            +
                              'method'  => 'foobar',
         | 
| 76 | 
            +
                              'id'      => 1
         | 
| 77 | 
            +
                            }
         | 
| 78 | 
            +
                      resp = JSON.parse(@sess.post('/', req.to_json).body)
         | 
| 79 | 
            +
                      resp.should == {
         | 
| 80 | 
            +
                                        'jsonrpc' => '2.0',
         | 
| 81 | 
            +
                                        'error'   => {
         | 
| 82 | 
            +
                                                        'code' => -32601,
         | 
| 83 | 
            +
                                                        'message' => 'Method not found.'
         | 
| 84 | 
            +
                                                      },
         | 
| 85 | 
            +
                                        'id'      => 1
         | 
| 86 | 
            +
                                      }
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  describe "receiving a call with the wrong number of params" do
         | 
| 91 | 
            +
                    it "returns an error response" do
         | 
| 92 | 
            +
                      req = {
         | 
| 93 | 
            +
                              'jsonrpc' => '2.0',
         | 
| 94 | 
            +
                              'method'  => 'subtract',
         | 
| 95 | 
            +
                              'params'  => [1,2,3],
         | 
| 96 | 
            +
                              'id'      => 1
         | 
| 97 | 
            +
                            }
         | 
| 98 | 
            +
                      resp = JSON.parse(@sess.post('/', req.to_json).body)
         | 
| 99 | 
            +
                      resp.should == {
         | 
| 100 | 
            +
                                        'jsonrpc' => '2.0',
         | 
| 101 | 
            +
                                        'error'   => {
         | 
| 102 | 
            +
                                                        'code' => -32602,
         | 
| 103 | 
            +
                                                        'message' => 'Invalid method parameter(s).'
         | 
| 104 | 
            +
                                                      },
         | 
| 105 | 
            +
                                        'id'      => 1
         | 
| 106 | 
            +
                                      }
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  describe "receiving invalid JSON" do
         | 
| 111 | 
            +
                    it "returns an error response" do
         | 
| 112 | 
            +
                      req = {
         | 
| 113 | 
            +
                              'jsonrpc' => '2.0',
         | 
| 114 | 
            +
                              'method'  => 'foobar',
         | 
| 115 | 
            +
                              'id'      => 1
         | 
| 116 | 
            +
                            }.to_json
         | 
| 117 | 
            +
                      req += '}' # make the json invalid
         | 
| 118 | 
            +
                      resp = JSON.parse(@sess.post('/', req).body)
         | 
| 119 | 
            +
                      resp.should == {
         | 
| 120 | 
            +
                                        'jsonrpc' => '2.0',
         | 
| 121 | 
            +
                                        'error'   => {
         | 
| 122 | 
            +
                                                        'code' => -32700,
         | 
| 123 | 
            +
                                                        'message' => 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.'
         | 
| 124 | 
            +
                                                      },
         | 
| 125 | 
            +
                                        'id'      => nil
         | 
| 126 | 
            +
                                      }
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  describe "receiving an invalid request" do
         | 
| 131 | 
            +
                    context "when the request is not a batch" do
         | 
| 132 | 
            +
                      it "returns an error response" do
         | 
| 133 | 
            +
                        req = {
         | 
| 134 | 
            +
                                'jsonrpc' => '2.0',
         | 
| 135 | 
            +
                                'method'  => 1 # method as int is invalid
         | 
| 136 | 
            +
                              }.to_json
         | 
| 137 | 
            +
                        resp = JSON.parse(@sess.post('/', req).body)
         | 
| 138 | 
            +
                        resp.should == INVALID_RESPONSE_EXPECTATION 
         | 
| 139 | 
            +
                      end
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                    context "when the request is an empty batch" do
         | 
| 143 | 
            +
                      it "returns an error response" do
         | 
| 144 | 
            +
                        req = [].to_json
         | 
| 145 | 
            +
                        resp = JSON.parse(@sess.post('/', req).body)
         | 
| 146 | 
            +
                        resp.should == INVALID_RESPONSE_EXPECTATION
         | 
| 147 | 
            +
                      end
         | 
| 148 | 
            +
                    end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                    context "when the request is an invalid batch" do
         | 
| 151 | 
            +
                      it "returns an error response" do
         | 
| 152 | 
            +
                        req = [1,2].to_json
         | 
| 153 | 
            +
                        resp = JSON.parse(@sess.post('/', req).body)
         | 
| 154 | 
            +
                        resp.should == [INVALID_RESPONSE_EXPECTATION, INVALID_RESPONSE_EXPECTATION] 
         | 
| 155 | 
            +
                      end
         | 
| 156 | 
            +
                    end
         | 
| 157 | 
            +
                  end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                  describe "receiving a valid batch request" do
         | 
| 160 | 
            +
                    context "when not all requests are notifications" do
         | 
| 161 | 
            +
                      it "returns an array of responses" do
         | 
| 162 | 
            +
                        reqs = [
         | 
| 163 | 
            +
                                  {'jsonrpc' => '2.0', 'method' => 'sum', 'params' => [1,2,4], 'id' => '1'},
         | 
| 164 | 
            +
                                  {'jsonrpc' => '2.0', 'method' => 'notify_hello', 'params' => [7]},
         | 
| 165 | 
            +
                                  {'jsonrpc' => '2.0', 'method' => 'subtract', 'params' => [42,23], 'id' => '2'},
         | 
| 166 | 
            +
                                  {'foo' => 'boo'},
         | 
| 167 | 
            +
                                  {'jsonrpc' => '2.0', 'method' => 'foo.get', 'params' => {'name' => 'myself'}, 'id' => '5'},
         | 
| 168 | 
            +
                                  {'jsonrpc' => '2.0', 'method' => 'get_data', 'id' => '9'} 
         | 
| 169 | 
            +
                               ].to_json
         | 
| 170 | 
            +
                        resp = JSON.parse(@sess.post('/', reqs).body)
         | 
| 171 | 
            +
                        resp.should == [
         | 
| 172 | 
            +
                                {'jsonrpc' => '2.0', 'result' => 7, 'id' => '1'},
         | 
| 173 | 
            +
                                {'jsonrpc' => '2.0', 'result' => 19, 'id' => '2'},
         | 
| 174 | 
            +
                                {'jsonrpc' => '2.0', 'error' => {'code' => -32600, 'message' => 'The JSON sent is not a valid Request object.'}, 'id' => nil},
         | 
| 175 | 
            +
                                {'jsonrpc' => '2.0', 'error' => {'code' => -32601, 'message' => 'Method not found.'}, 'id' => '5'},
         | 
| 176 | 
            +
                                {'jsonrpc' => '2.0', 'result' => ['hello', 5], 'id' => '9'}
         | 
| 177 | 
            +
                        ]
         | 
| 178 | 
            +
                      end
         | 
| 179 | 
            +
                    end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                    context "when all the requests are notifications" do
         | 
| 182 | 
            +
                      it "returns no response" do
         | 
| 183 | 
            +
                        req = [
         | 
| 184 | 
            +
                                {
         | 
| 185 | 
            +
                                  'jsonrpc' => '2.0',
         | 
| 186 | 
            +
                                  'method'  => 'update',
         | 
| 187 | 
            +
                                  'params'  => [1,2,3,4,5]
         | 
| 188 | 
            +
                                },
         | 
| 189 | 
            +
                                {
         | 
| 190 | 
            +
                                  'jsonrpc' => '2.0',
         | 
| 191 | 
            +
                                  'method'  => 'update',
         | 
| 192 | 
            +
                                  'params'  => [1,2,3,4,5]
         | 
| 193 | 
            +
                                }
         | 
| 194 | 
            +
                              ]
         | 
| 195 | 
            +
                        resp = @sess.post('/', req.to_json).body
         | 
| 196 | 
            +
                        resp.should be_empty
         | 
| 197 | 
            +
                      end
         | 
| 198 | 
            +
                    end
         | 
| 199 | 
            +
                  end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
              end
         | 
| 202 | 
            +
            end
         | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            require 'rubygems'
         | 
| 2 | 
            +
            $:.unshift(File.dirname(__FILE__) + '/../lib/')
         | 
| 3 | 
            +
            require 'jimson'
         | 
| 4 | 
            +
            require 'open-uri'
         | 
| 5 | 
            +
            require 'json'
         | 
| 6 | 
            +
            require 'patron'
         | 
| 7 | 
            +
            require 'fakeweb'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            SPEC_URL = 'http://localhost:8999'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
             | 
| 12 | 
            +
            def fake_response(json)
         | 
| 13 | 
            +
              FakeWeb.register_uri(:post, SPEC_URL, :body => json)
         | 
| 14 | 
            +
            end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            pid = Process.spawn(File.dirname(__FILE__) + '/em_helper.rb')
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            RSpec.configure do |config|
         | 
| 19 | 
            +
              config.after(:all) do
         | 
| 20 | 
            +
                Process.kill(9, pid)
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            sleep 1
         | 
| 25 | 
            +
             | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,117 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification 
         | 
| 2 | 
            +
            name: jimson
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            +
              prerelease: 
         | 
| 5 | 
            +
              version: 0.1.0
         | 
| 6 | 
            +
            platform: ruby
         | 
| 7 | 
            +
            authors: 
         | 
| 8 | 
            +
            - Chris Kite
         | 
| 9 | 
            +
            autorequire: 
         | 
| 10 | 
            +
            bindir: bin
         | 
| 11 | 
            +
            cert_chain: []
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            date: 2011-07-19 00:00:00 -05:00
         | 
| 14 | 
            +
            default_executable: 
         | 
| 15 | 
            +
            dependencies: 
         | 
| 16 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 17 | 
            +
              name: patron
         | 
| 18 | 
            +
              prerelease: false
         | 
| 19 | 
            +
              requirement: &id001 !ruby/object:Gem::Requirement 
         | 
| 20 | 
            +
                none: false
         | 
| 21 | 
            +
                requirements: 
         | 
| 22 | 
            +
                - - ">="
         | 
| 23 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 24 | 
            +
                    version: 0.4.12
         | 
| 25 | 
            +
              type: :runtime
         | 
| 26 | 
            +
              version_requirements: *id001
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 28 | 
            +
              name: eventmachine
         | 
| 29 | 
            +
              prerelease: false
         | 
| 30 | 
            +
              requirement: &id002 !ruby/object:Gem::Requirement 
         | 
| 31 | 
            +
                none: false
         | 
| 32 | 
            +
                requirements: 
         | 
| 33 | 
            +
                - - ">="
         | 
| 34 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 35 | 
            +
                    version: 0.12.10
         | 
| 36 | 
            +
              type: :runtime
         | 
| 37 | 
            +
              version_requirements: *id002
         | 
| 38 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 39 | 
            +
              name: eventmachine_httpserver
         | 
| 40 | 
            +
              prerelease: false
         | 
| 41 | 
            +
              requirement: &id003 !ruby/object:Gem::Requirement 
         | 
| 42 | 
            +
                none: false
         | 
| 43 | 
            +
                requirements: 
         | 
| 44 | 
            +
                - - ">="
         | 
| 45 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 46 | 
            +
                    version: 0.2.1
         | 
| 47 | 
            +
              type: :runtime
         | 
| 48 | 
            +
              version_requirements: *id003
         | 
| 49 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 50 | 
            +
              name: json
         | 
| 51 | 
            +
              prerelease: false
         | 
| 52 | 
            +
              requirement: &id004 !ruby/object:Gem::Requirement 
         | 
| 53 | 
            +
                none: false
         | 
| 54 | 
            +
                requirements: 
         | 
| 55 | 
            +
                - - ">="
         | 
| 56 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 57 | 
            +
                    version: 1.5.1
         | 
| 58 | 
            +
              type: :runtime
         | 
| 59 | 
            +
              version_requirements: *id004
         | 
| 60 | 
            +
            description: 
         | 
| 61 | 
            +
            email: 
         | 
| 62 | 
            +
            executables: []
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            extensions: []
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            extra_rdoc_files: 
         | 
| 67 | 
            +
            - README.rdoc
         | 
| 68 | 
            +
            files: 
         | 
| 69 | 
            +
            - VERSION
         | 
| 70 | 
            +
            - LICENSE.txt
         | 
| 71 | 
            +
            - CHANGELOG.rdoc
         | 
| 72 | 
            +
            - README.rdoc
         | 
| 73 | 
            +
            - Rakefile
         | 
| 74 | 
            +
            - lib/jimson.rb
         | 
| 75 | 
            +
            - lib/jimson/client_error.rb
         | 
| 76 | 
            +
            - lib/jimson/server.rb
         | 
| 77 | 
            +
            - lib/jimson/server_error.rb
         | 
| 78 | 
            +
            - lib/jimson/response.rb
         | 
| 79 | 
            +
            - lib/jimson/request.rb
         | 
| 80 | 
            +
            - lib/jimson/client.rb
         | 
| 81 | 
            +
            - spec/server_spec.rb
         | 
| 82 | 
            +
            - spec/client_spec.rb
         | 
| 83 | 
            +
            - spec/em_helper.rb
         | 
| 84 | 
            +
            - spec/spec_helper.rb
         | 
| 85 | 
            +
            has_rdoc: true
         | 
| 86 | 
            +
            homepage: http://www.github.com/chriskite/jimson
         | 
| 87 | 
            +
            licenses: []
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            post_install_message: 
         | 
| 90 | 
            +
            rdoc_options: []
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            require_paths: 
         | 
| 93 | 
            +
            - lib
         | 
| 94 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         | 
| 95 | 
            +
              none: false
         | 
| 96 | 
            +
              requirements: 
         | 
| 97 | 
            +
              - - ">="
         | 
| 98 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 99 | 
            +
                  version: "0"
         | 
| 100 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 101 | 
            +
              none: false
         | 
| 102 | 
            +
              requirements: 
         | 
| 103 | 
            +
              - - ">="
         | 
| 104 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 105 | 
            +
                  version: "0"
         | 
| 106 | 
            +
            requirements: []
         | 
| 107 | 
            +
             | 
| 108 | 
            +
            rubyforge_project: 
         | 
| 109 | 
            +
            rubygems_version: 1.6.2
         | 
| 110 | 
            +
            signing_key: 
         | 
| 111 | 
            +
            specification_version: 3
         | 
| 112 | 
            +
            summary: JSON-RPC 2.0 client and server
         | 
| 113 | 
            +
            test_files: 
         | 
| 114 | 
            +
            - spec/server_spec.rb
         | 
| 115 | 
            +
            - spec/client_spec.rb
         | 
| 116 | 
            +
            - spec/em_helper.rb
         | 
| 117 | 
            +
            - spec/spec_helper.rb
         |