Skip to content

Commit

Permalink
Merge pull request #91 from katylouise/katylouise/update-documentation
Browse files Browse the repository at this point in the history
Updated documentation; updated readme; updated ruby version and gems
  • Loading branch information
katylouise authored Feb 12, 2019
2 parents e1a1ee0 + dbbf8f5 commit 885a30a
Show file tree
Hide file tree
Showing 7 changed files with 22 additions and 186 deletions.
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.6.0
2.6.1
171 changes: 3 additions & 168 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Parliament Data API Wrapper (Ruby)
[parliament-ruby][parliament-ruby] is a gem created by the [Parliamentary Digital Service][pds] to allow easy communication with the internal parliament data api.
[parliament-ruby][parliament-ruby] is a gem created by the [Parliamentary Digital Service][pds] to allow easy communication with the internal parliament data API.

[![Gem][shield-gem]][info-gem] [![Build Status][shield-travis]][info-travis] [![Test Coverage][shield-coveralls]][info-coveralls] [![License][shield-license]][info-license]

Expand Down Expand Up @@ -40,14 +40,13 @@


## Installation
This gem is currently not available on RubyGems. To use it in an application, install it directly from GitHub via your Gemfile
```bash
gem 'parliament', git: 'https://github.com/ukparliament/parliament-ruby.git', branch: 'master'
gem 'parliament-ruby'
```


## Usage
This gem's main function is fetching an n-triple file from a remote server and converting it into linked ruby objects.
This gem's main function is fetching an n-triples from a remote server and converting it into linked ruby objects.

> **Note:** Comprehensive class documentation can be found on [rubydocs][rubydocs].
Expand Down Expand Up @@ -111,170 +110,6 @@ Parliament::Request.new.headers #=> { 'Accept' => 'Test' }
# You can still override the headers on an instance by instance basis
Parliament::Request.new(headers: { 'Accept' => 'Test2' }).headers #=> { 'Accept' => 'Test2' }
```
### Methods
[parliament-ruby][parliament-ruby] comes with the following common methods:

| Method | Description |
|----------------------------------------|-------------|
| [`#get`](#get) | **Make a GET request** - Make a HTTP GET request to the endpoint we have built, and create Ruby objects. |
| [`#filter`](#filter) | **Filter the response** - After making a GET request, filter the objects returned by type attribute. |
| [`#sort_by`](#sort_by) | **Sort the response (ASC)** - After making a GET request, sort the result in ascending order. |
| [`#reverse_sort_by`](#reverse_sort_by) | **Sort the response (DESC)** - After making a GET request, sort the result in descending order. |

> **Note:** Comprehensive class documentation can be found on [rubydocs][rubydocs].
#### `#get`
Once you've built your endpoint (`parliament.people.current`), we use the `#get` method to tell us you're ready to get the data.

```ruby
# Target endpoint: 'http://test.com/people/123/letters/456'
response = parliament.people('123').letters('456').get #=> #<Parliament::Response [...]>

response.each do |node|
# If your n-triple file contains a literal object it is stored into an instance variable accessible via the predicate
# name. For example, with the following triple:
# <http://id.ukpds.org/1234> <http://id.ukpds.org/schema/name> 'Matthew Rayner' .
#
# You would be able to access the `name` attribute like so:
puts node.name #=> 'Matthew Rayner'

# If your n-triple file contains a triple who's object is a URI, and that URI is defined within your file, a link will
# be created, allowing you to 'connect' the two objects. For example, with the following triples:
# <http://id.ukpds.org/1234> <http://id.ukpds.org/schema/name> 'Matthew Rayner' .
# <http://id.ukpds.org/1234> <http://id.ukpds.org/schema/partyMembership> <http://id.ukpds.org/5678> .
# <http://id.ukpds.org/5678> <http://id.ukpds.org/schema/startDate> "1992-04-09"^^<http://www.w3.org/2001/XMLSchema#date> .
#
# You would be able to access the start date attribute on the linked object like so:
puts node.graph_id #=> '12345'
puts node.name #=> 'Matthew Rayner'
puts node.partyMembership #=> [#<Grom::Node @startDate=...>]

puts node.partyMembership.first.startDate #=> "1992-04-09"
end
```

`#get` returns a `Parliament::Response` object which contains all of the nodes from our n-triple response.

#### `#filter`
If your n-triple file contains a number of different objects you can filter based on type attribute.

```ruby
# Target endpoint: 'http://test.com/people/members/current'
response = parliament.people.members.current.get #=> #<Parliament::Response [...]>

# Given the below set of triples, you will be able to filter on 'type' attribute.
# <http://id.ukpds.org/cea89432-e046-4013-a9ba-e93d0468d186> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://id.ukpds.org/schema/Person> .
# <http://id.ukpds.org/4ef6c7b7-a5c8-4dde-a5a5-29c9f80d8a27> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://id.ukpds.org/schema/Constituency> .
# <http://id.ukpds.org/80234c90-f86a-4942-b6ae-d1e57a0b378d> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://id.ukpds.org/schema/Party> .
# <http://id.ukpds.org/HouseOfCommons> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://id.ukpds.org/schema/House> .
#
# The filter method returns an array of filtered responses. Each response holding just items with the given type attribute.
filtered_responses = response.filter('http://id.ukpds.org/schema/Person', 'http://id.ukpds.org/schema/Party', 'http://id.ukpds.org/schema/Constituency')
filtered_responses #=> [<#Parliament::Response [...]>, <#Parliament::Response []>, <#Parliament::Response [...]>]
```

#### `#sort_by`
Once you have a `Parliament::Response` object, you can perform an ascending sort on the results by using the `#sort_by` method.

> **`#sort_by`** is an ascending order sort i.e. (a..z, 0..9)
```ruby
# Target endpoint: 'http://test.com/people/members/current'
response = parliament.people.members.current.get #=> #<Parliament::Response [...]>

# Given the below set of triples, you can sort ascending like so:
# <http://id.ukpds.org/1234> <http://id.ukpds.org/schema/startDate> "1997-01-01"^^<http://www.w3.org/2001/XMLSchema#date> .
# <http://id.ukpds.org/5678> <http://id.ukpds.org/schema/startDate> "1991-03-15"^^<http://www.w3.org/2001/XMLSchema#date> .
# <http://id.ukpds.org/9101> <http://id.ukpds.org/schema/startDate> "2011-09-04"^^<http://www.w3.org/2001/XMLSchema#date> .
# <http://id.ukpds.org/1121> <http://id.ukpds.org/schema/startDate> "1981-07-31"^^<http://www.w3.org/2001/XMLSchema#date> .
# <http://id.ukpds.org/3141> <http://id.ukpds.org/schema/endDate> "1997-07-31"^^<http://www.w3.org/2001/XMLSchema#date> .
#
# The sort_by method returns a sorted array of Grom::Nodes, sorted by one, or many, symbols passed to it.
sorted_response = response.sort_by(:startDate)

# Output each of the graph_id and startDate values
sorted_response.each { |node| puts "#{node.graph_id} - #{node.respond_to?(:startDate) ? node.startDate : 'undefined'}" }
# http://id.ukpds.org/3141 - undefined
# http://id.ukpds.org/1121 - 1981-07-31
# http://id.ukpds.org/5678 - 1991-03-15
# http://id.ukpds.org/1234 - 1997-01-01
# http://id.ukpds.org/9101 - 2011-09-04
```

> **NOTE:** `#sort_by` places all `nil` responses at the start of the sort. For a more custom sort, take a look at `Parliament::Utils.sort_by`.
#### `#reverse_sort_by`
`#reverse_sort_by` simple implements `#sort_by` and calls `#reverse!` on the resulting array. The above example would become:

```ruby
# http://id.ukpds.org/9101 - 2011-09-04
# http://id.ukpds.org/1234 - 1997-01-01
# http://id.ukpds.org/5678 - 1991-03-15
# http://id.ukpds.org/1121 - 1981-07-31
# http://id.ukpds.org/3141 - undefined
```

### `Parliament::Utils`
Included with [parliament-ruby][parliament-ruby] is `Parliament::Utils`. This module includes helper methods commonly used throughout parliament.uk.

#### Methods

| Method | Description |
|-------------------------------------------------------|-------------|
| [`#sort_by`](#parliamentutilssort_by) | **Sort an enumerable thing (ASC)** - An implementation of ruby's `#sort_by` method that allows nil sorts. |
| [`#reverse_sort_by`](#parliamentutilsreverse_sort_by) | **Sort an enumerable thing (DESC)** - Reverse the result of `Parliament::Utils.reverse_sort_by` |


##### `Parliament::Utils.sort_by`
One of the common use cases we have is sorting objects by a date (e.g. seat incumbency end date) where an object without an end date is considered 'current' and should be sorted to the top (or bottom) of a list.

Because we working with graph databases, a node without an endDate simply has no method when converted to an object with [GROM][grom].

The `Parliament::Utils.sort_by` method takes a hash of options, detailed below:

```ruby
response = parliament.people('123').get.filter('http://id.ukpds.org/schema/Person')

objects = response.first.incumbencies

options = {
list: objects, # An enumerable of objects (most commonly an Array)
parameters: [:endDate], # An array of actions which we will sort by
prepend_rejected: false # {optional default=true} Should any objects that are 'rejected' be prepended to the sorted list, or appended. i.e. where to put objects that don't respond to parameters provided
}

sorted_list = Parliament::Util.sort_by(options)

sorted_list.each { |incumbency| puts incumbency.respond_to?(:endDate) ? incumbency.endDate : 'Current' }
# http://id.ukpds.org/1121 - 1981-07-31
# http://id.ukpds.org/5678 - 1991-03-15
# http://id.ukpds.org/1234 - 1997-01-01
# http://id.ukpds.org/9101 - 2011-09-04
# http://id.ukpds.org/3141 - Current
```

##### `Parliament::Utils.reverse_sort_by`
This method, under the hood, calls `Parliament::Utils.sort_by` and runs `#reverse!` on the result.

Following the above example, and changing:
```ruby
sorted_list = Parliament::Util.sort_by(options)
```

to:
```ruby
sorted_list = Parliament::Util.reverse_sort_by(options)
```

should result in:
```ruby
# http://id.ukpds.org/3141 - Current
# http://id.ukpds.org/9101 - 2011-09-04
# http://id.ukpds.org/1234 - 1997-01-01
# http://id.ukpds.org/5678 - 1991-03-15
# http://id.ukpds.org/1121 - 1981-07-31
```


## Getting Started with Development
To clone the repository and set up the dependencies, run the following:
Expand Down
2 changes: 1 addition & 1 deletion lib/parliament/builder/base_response_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Builder
# Base response builder, allowing the user to return the body of an HTTPResponse.
# @since 0.7.5
class BaseResponseBuilder
# Creates a new BaseReponseBuilder.
# Creates a new BaseResponseBuilder.
# @param [HTTPResponse] response an HTTP response.
# @param [Module] decorators a namespace which contains modules used to decorate the objects we receive. It is not used directly by the BaseResponseBuilder, but is there for API completeness.
def initialize(response:, decorators: nil)
Expand Down
21 changes: 12 additions & 9 deletions lib/parliament/request/base_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ module Request
#
# @since 0.7.5
#
# @attr_reader [String] base_url the base url of our api. (expected: http://example.com - without the trailing slash).
# @attr_reader [String] base_url the base url of our api. (eg: http://example.com - without the trailing slash).
# @attr_reader [Hash] headers the headers being sent in the request.
# @attr_reader [Hash] query_params any query parameters to be sent in the request.
class BaseRequest
TIMEOUT = 40.freeze
CONNECTTIMEOUT = 5.freeze
Expand All @@ -17,7 +18,7 @@ class BaseRequest
#
# An interesting note for #initialize is that setting base_url on the class, or using the environment variable
# PARLIAMENT_BASE_URL means you don't need to pass in a base_url. You can pass one anyway to override the
# environment variable or class parameter. Similarly, headers can be set by either settings the headers on the class, or passing headers in.
# environment variable or class parameter. Similarly, headers can be set by either setting the headers on the class, or passing headers in.
#
# @example Setting the base_url on the class
# Parliament::Request::BaseRequest.base_url = 'http://example.com'
Expand Down Expand Up @@ -58,7 +59,7 @@ class BaseRequest
# @param [String] base_url the base url of our api. (expected: http://example.com - without the trailing slash).
# @param [Hash] headers the headers being sent in the request.
# @param [Parliament::Builder] builder the builder to use in order to build a response.
# @params [Module] decorators the decorator module to use in order to provide possible alias methods for any objects created by the builder.
# @param [Module] decorators the decorator modules to use in order to provide possible alias methods for any objects created by the builder.
def initialize(base_url: nil, headers: nil, builder: nil, decorators: nil)
@base_url = base_url || self.class.base_url
@headers = headers || self.class.headers || {}
Expand All @@ -70,7 +71,7 @@ def initialize(base_url: nil, headers: nil, builder: nil, decorators: nil)
# Makes an HTTP GET request and process results into a response.
#
# @example HTTP GET request
# request = Parliament::Request::BaseRequest.new(base_url: 'http://example.com/people/123'
# request = Parliament::Request::BaseRequest.new(base_url: 'http://example.com/people/123')
#
# # url: http://example.com/people/123
#
Expand All @@ -88,6 +89,8 @@ def initialize(base_url: nil, headers: nil, builder: nil, decorators: nil)
# @raise [Parliament::NoContentResponseError] when the response body is empty.
#
# @param [Hash] params (optional) additional URI encoded form values to be added to the URI.
# @param [Integer] timeout (optional) time limit for the entire request in seconds.
# @param [Integer] connecttimeout (optional) time limit for just the connection in seconds.
#
# @return [Parliament::Response::BaseResponse] a Parliament::Response::BaseResponse object containing all of the data returned from the URL.
def get(params: nil, timeout: TIMEOUT, connecttimeout: CONNECTTIMEOUT)
Expand Down Expand Up @@ -134,7 +137,8 @@ def get(params: nil, timeout: TIMEOUT, connecttimeout: CONNECTTIMEOUT)
#
# @param [Hash] params (optional) additional URI encoded form values to be added to the URI.
# @param [String] body (optional) body of the post request.
# @param [Integer] timeout (optional) a Net::HTTP.read_timeout value passed suring the post.
# @param [Integer] timeout (optional) time limit for the entire request in seconds.
# @param [Integer] connecttimeout (optional) time limit for just the connection in seconds.
#
# @return [Parliament::Response::BaseResponse] a Parliament::Response::BaseResponse object containing all of the data returned from the URL.
def post(params: nil, body: nil, timeout: TIMEOUT, connecttimeout: CONNECTTIMEOUT)
Expand Down Expand Up @@ -162,7 +166,7 @@ def post(params: nil, body: nil, timeout: TIMEOUT, connecttimeout: CONNECTTIMEOU

private

# @attr [String] base_url the base url of our api. (expected: http://example.com - without the trailing slash).
# @attr [String] base_url the base url of our api. (eg: http://example.com - without the trailing slash).
# @attr [Hash] headers the headers being sent in the request.
class << self
attr_accessor :base_url, :headers
Expand All @@ -187,7 +191,7 @@ def headers
def handle_errors(response)
exception_class = if response.success? # 2xx Status
Parliament::NoContentResponseError if response.headers&.[]('Content-Length') == '0' ||
(response.headers&.[]('Content-Length').nil? && response.body.empty?)
(response.headers&.[]('Content-Length').nil? && response.body.empty?)
elsif /\A4\w{2}/.match(response.code.to_s) # 4xx Status
Parliament::ClientError
elsif /\A5\w{2}/.match(response.code.to_s) # 5xx Status
Expand All @@ -205,7 +209,7 @@ def separate_uri(query_url, query_params, additional_params)
if endpoint.query
# Returns [ ["key", "value"], ["key", "value"] ]
key_value_array = URI.decode_www_form(endpoint.query)
key_value_array.map! { |key_value_pair| [ key_value_pair[0].to_sym, key_value_pair[1] ] }
key_value_array.map! { |key_value_pair| [key_value_pair[0].to_sym, key_value_pair[1]] }
temp_params = key_value_array.to_h
end

Expand All @@ -219,4 +223,3 @@ def separate_uri(query_url, query_params, additional_params)
end
end
end

6 changes: 2 additions & 4 deletions lib/parliament/request/url_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ module Request
#
# @since 0.7.5
#
# @attr_reader [String] base_url the endpoint for our API which we will build our requests on. (expected: http://example.com - without the trailing slash).
# @attr_reader [Hash] headers the headers being sent in the request.
class UrlRequest < Parliament::Request::BaseRequest
# Creates a new instance of Parliament::Request::UrlRequest.
#
Expand All @@ -26,7 +24,7 @@ def initialize(base_url: nil, headers: nil, builder: nil, decorators: nil)
super
end

# Overrides ruby's method_missing to allow creation of URLs through method calls.
# Overrides Ruby's method_missing to allow creation of URLs through method calls.
#
# @example Adding a simple URL part
# request = Parliament::Request::UrlRequest.new(base_url: 'http://example.com')
Expand All @@ -48,7 +46,7 @@ def initialize(base_url: nil, headers: nil, builder: nil, decorators: nil)
#
# @param [Symbol] method the 'method' (url part) we are processing.
# @param [Array<Object>] params parameters passed to the specified method (url part).
# @param [Block] block additional block (kept for compatibility with method_missing API).
# @param [Block] block additional block (kept for compatibility with method_missing interface).
#
# @return [Parliament::Request::UrlRequest] self (this is to allow method chaining).
def method_missing(method, *params, &block)
Expand Down
2 changes: 1 addition & 1 deletion lib/parliament/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Parliament
VERSION = '1.0.1'.freeze
VERSION = '1.0.2'.freeze
end
4 changes: 2 additions & 2 deletions parliament-ruby.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ require 'parliament/version'
Gem::Specification.new do |spec|
spec.name = 'parliament-ruby'
spec.version = Parliament::VERSION
spec.authors = ['Matt Rayner']
spec.email = ['[email protected]']
spec.authors = ['Matt Rayner', 'Rebecca Appleyard', 'Giuseppe De Santis']
spec.email = ['[email protected]', '[email protected]']
spec.summary = %q{Internal parliamentary API wrapper}
spec.description = %q{Internal parliamentary data API wrapper for ruby}
spec.homepage = 'http://github.com/ukparliament/parliament_ruby'
Expand Down

0 comments on commit 885a30a

Please sign in to comment.