festivals_lab 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.rvmrc ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
+ # Only full ruby name is supported here, for short names use:
8
+ # echo "rvm use 1.9.3" > .rvmrc
9
+ environment_id="ruby-1.9.3-p327@festivalslab"
10
+
11
+ # Uncomment the following lines if you want to verify rvm version per project
12
+ # rvmrc_rvm_version="1.17.0 (master)" # 1.10.1 seams as a safe start
13
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
+ # return 1
16
+ # }
17
+
18
+ # First we attempt to load the desired environment directly from the environment
19
+ # file. This is very fast and efficient compared to running through the entire
20
+ # CLI and selector. If you want feedback on which environment was used then
21
+ # insert the word 'use' after --create as this triggers verbose mode.
22
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
+ then
25
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
+ [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
27
+ \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
28
+ if [[ $- == *i* ]] # check for interactive shells
29
+ then echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
30
+ else echo "Using: $GEM_HOME" # don't use colors in non-interactive shells
31
+ fi
32
+ else
33
+ # If the environment file has not yet been created, use the RVM CLI to select.
34
+ rvm --create use "$environment_id" || {
35
+ echo "Failed to create RVM environment '${environment_id}'."
36
+ return 1
37
+ }
38
+ fi
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ festivals_lab (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ addressable (2.3.5)
10
+ ansi (1.4.3)
11
+ builder (3.2.2)
12
+ crack (0.4.1)
13
+ safe_yaml (~> 0.9.0)
14
+ hashie (2.0.5)
15
+ minitest (4.7.5)
16
+ minitest-reporters (0.14.20)
17
+ ansi
18
+ builder
19
+ minitest (>= 2.12, < 5.0)
20
+ powerbar
21
+ powerbar (1.0.11)
22
+ ansi (~> 1.4.0)
23
+ hashie (>= 1.1.0)
24
+ rake (10.0.2)
25
+ safe_yaml (0.9.5)
26
+ webmock (1.13.0)
27
+ addressable (>= 2.2.7)
28
+ crack (>= 0.3.2)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ festivals_lab!
35
+ minitest-reporters
36
+ rake
37
+ webmock
File without changes
@@ -0,0 +1,11 @@
1
+ require 'rake/testtask'
2
+
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/**/test*.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ task :default => [:test]
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/festivals_lab/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Gareth Adams"]
6
+ gem.email = ["g@rethada.ms"]
7
+ gem.description = %q{Accesses festival data from the Edinburgh Festivals Innovation Lab API}
8
+ gem.summary = %q{The Edinburgh Festivals API is an ambitious project which aims to create open access to the event listings data of Edinburgh’s 12 major Festivals}
9
+ gem.homepage = "http://festivalslab.com/"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "festivals_lab"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = FestivalsLab::VERSION
17
+
18
+ gem.add_development_dependency 'rake'
19
+ gem.add_development_dependency 'webmock'
20
+ gem.add_development_dependency 'minitest-reporters'
21
+ end
@@ -0,0 +1,89 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'openssl' # Provides HMAC-SHA1
4
+
5
+ class FestivalsLab
6
+
7
+ attr_accessor :access_token, :access_key
8
+
9
+ SCHEME = "http"
10
+ HOST = "api.festivalslab.com"
11
+
12
+ def initialize access_token, access_key
13
+ @access_token = access_token
14
+ @access_key = access_key
15
+ end
16
+
17
+ def events params = {}
18
+ params = params.dup
19
+
20
+ # Exact field options
21
+ valid_options = [:festival, :genre, :country, :code, :year]
22
+ # Fulltext search options
23
+ valid_options += [:title, :description, :artist]
24
+ # Event date options
25
+ valid_options += [:date_from, :date_to]
26
+ # Venue options
27
+ valid_options += [:venue_name, :venue_code, :post_code, :distance, :lat, :lng]
28
+ # Price options
29
+ valid_options += [:price_from, :price_to]
30
+ # Update options
31
+ valid_options += [:modified_from]
32
+ # Pagination options
33
+ valid_options += [:size, :from]
34
+
35
+ invalid_keys = params.reject { |k,v| valid_options.include? k }.keys
36
+
37
+ raise ArgumentError, "Unexpected events parameter: #{invalid_keys.join ", "}" if invalid_keys.any?
38
+
39
+ request '/events', params
40
+ end
41
+
42
+ def request endpoint, params = {}
43
+ uri = signed_uri endpoint, params
44
+ Net::HTTP.start(uri.host, uri.port) do |http|
45
+ request = Net::HTTP::Get.new uri.request_uri
46
+ request['Accept'] = 'application/json'
47
+ response = http.request request
48
+ if Net::HTTPSuccess === response
49
+ JSON.parse(response.body)
50
+ else
51
+ raise ApiError.new(response)
52
+ end
53
+ end
54
+ end
55
+
56
+ def signed_uri endpoint, params = {}
57
+ params = params.dup
58
+ raise Error, "Missing API access key" unless access_key
59
+ raise Error, "Missing API access token" unless access_token
60
+ # Start with a generic URI representing just the path
61
+ # This is convenient because the URI represents the string which gets signed
62
+ uri = URI(endpoint)
63
+
64
+ params[:key] = access_token
65
+ uri.query = URI.encode_www_form(params)
66
+
67
+ params[:signature] = self.signature uri.to_s
68
+ uri.query = URI.encode_www_form(params)
69
+
70
+ uri.scheme = SCHEME
71
+ uri.host = HOST
72
+ # Now the URI has a scheme we can convert it to an actual URI::HTTP
73
+ URI.parse(uri.to_s)
74
+ end
75
+
76
+ def signature url
77
+ OpenSSL::HMAC.hexdigest 'sha1', access_key, url
78
+ end
79
+
80
+ Error = Class.new(StandardError)
81
+ ArgumentError = Class.new(::ArgumentError)
82
+ ApiError = Class.new(StandardError) do
83
+ attr_reader :response
84
+
85
+ def initialize response
86
+ @response = response
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ class FestivalsLab
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,144 @@
1
+ require 'test_helper'
2
+
3
+ require 'minitest/spec'
4
+ require 'webmock/minitest'
5
+
6
+ require 'festivals_lab'
7
+
8
+ describe FestivalsLab do
9
+
10
+ before do
11
+ @api = FestivalsLab.new '123', '456'
12
+ @successful_response = { status: 200, body: "[{}]", headers: { 'Content-Type' => 'application/json' } }
13
+ @failed_response = { status: 403, body: "", headers: { 'Content-Type' => 'application/json' } }
14
+ end
15
+
16
+ it "has a VERSION" do
17
+ FestivalsLab.constants.must_include :VERSION
18
+ end
19
+
20
+ describe "#initialize" do
21
+ it "sets an access token" do
22
+ api = FestivalsLab.new "123", nil
23
+
24
+ api.access_token.must_equal "123"
25
+ end
26
+
27
+ it "sets an access key" do
28
+ api = FestivalsLab.new nil, "456"
29
+
30
+ api.access_key.must_equal "456"
31
+ end
32
+ end
33
+
34
+ describe "#events" do
35
+ [:festival, :genre, :country, :code, :year, :title, :description, :artist, :venue_name, :post_code, :distance].each do |param|
36
+ it "allows a string '#{param}' parameter" do
37
+ stub = stub_http_request(:get, %r{//api\.festivalslab\.com/events})
38
+ .with(query: hash_including(param => 'value'))
39
+ .to_return(@successful_response)
40
+
41
+ @api.events param => 'value'
42
+
43
+ assert_requested(stub)
44
+ end
45
+ end
46
+
47
+ [:venue_code, :price_from, :price_to, :size, :from, :lat, :lng].each do |param|
48
+ it "allows a numeric '#{param}' parameter" do
49
+ stub = stub_http_request(:get, %r{//api\.festivalslab\.com/events})
50
+ .with(query: hash_including(param => '42'))
51
+ .to_return(@successful_response)
52
+
53
+ @api.events param => 42
54
+
55
+ assert_requested(stub)
56
+ end
57
+ end
58
+
59
+ [:date_from, :date_to, :modified_from].each do |param|
60
+ it "allows a date '#{param}' parameter" do
61
+ stub = stub_http_request(:get, %r{//api\.festivalslab\.com/events})
62
+ .with(query: hash_including(param => '1970-01-01 01:00:00'))
63
+ .to_return(@successful_response)
64
+
65
+ @api.events param => Time.at(0).strftime("%F %T")
66
+
67
+ assert_requested(stub)
68
+ end
69
+ end
70
+
71
+ it "returns parsed events" do
72
+ stub = stub_http_request(:get, %r{//api\.festivalslab\.com/events})
73
+ .to_return(@successful_response)
74
+
75
+ response = @api.events
76
+
77
+ assert_equal [{}], response
78
+ end
79
+
80
+ it "rejects invalid parameters" do
81
+ lambda {
82
+ @api.events foobar: 42
83
+ }.must_raise(FestivalsLab::ArgumentError, "Unexpected events parameter: foobar")
84
+ end
85
+ end
86
+
87
+ describe "#request" do
88
+ before do
89
+ @uri = "http://api.festivalslab.com/events?key=123&signature=742969faae86a4ba4223c7d93d05ead4b1397c23"
90
+ end
91
+
92
+ it "makes a request to the signed endpoint URI" do
93
+ stub_request(:get, @uri).to_return(@successful_response)
94
+
95
+ @api.request '/events'
96
+
97
+ assert_requested(:get, @uri)
98
+ end
99
+
100
+ it "parses the response as JSON" do
101
+ stub = stub_http_request(:get, @uri)
102
+ .to_return(@successful_response)
103
+
104
+ response = @api.request '/events'
105
+
106
+ assert_equal [{}], response
107
+ end
108
+
109
+ it "raises FestivalsLab::ApiError on unsuccessful HTTP request" do
110
+ stub = stub_http_request(:get, @uri)
111
+ .to_return(@failed_response)
112
+
113
+ lambda { @api.request '/events' }.must_raise(FestivalsLab::ApiError)
114
+ end
115
+ end
116
+
117
+ describe "#signed_uri" do
118
+ it "requires an access key to be set" do
119
+ @api.access_key = nil
120
+ lambda { @api.signed_uri('/events') }.must_raise(FestivalsLab::Error, "Missing API access key")
121
+ end
122
+
123
+ it "requires an access token to be set" do
124
+ @api.access_token = nil
125
+ lambda { @api.signed_uri('/events') }.must_raise(FestivalsLab::Error, "Missing API access token")
126
+ end
127
+
128
+ it "appends the correct signature to the request URI" do
129
+ uri = @api.signed_uri('/events', {:key => 123})
130
+ uri.must_be_kind_of(URI)
131
+ uri.to_s.must_equal 'http://api.festivalslab.com/events?key=123&signature=742969faae86a4ba4223c7d93d05ead4b1397c23'
132
+ end
133
+ end
134
+
135
+ describe "#signature" do
136
+ it "calculates the HMAC-SHA1 signature based on the access key" do
137
+ ## Because `OpenSSL#HMAC.hexdigest('sha1', '456', '/events?key=123')` # => '742969faae86a4ba4223c7d93d05ead4b1397c23'
138
+ @api.signature('/events?key=123').must_equal '742969faae86a4ba4223c7d93d05ead4b1397c23'
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+
@@ -0,0 +1,4 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/reporters'
3
+
4
+ MiniTest::Reporters.use!
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: festivals_lab
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gareth Adams
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: webmock
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: minitest-reporters
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Accesses festival data from the Edinburgh Festivals Innovation Lab API
63
+ email:
64
+ - g@rethada.ms
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .rvmrc
70
+ - Gemfile
71
+ - Gemfile.lock
72
+ - README.md
73
+ - Rakefile
74
+ - festivals_lab.gemspec
75
+ - lib/festivals_lab.rb
76
+ - lib/festivals_lab/version.rb
77
+ - test/lib/test_festivals_lab.rb
78
+ - test/test_helper.rb
79
+ homepage: http://festivalslab.com/
80
+ licenses: []
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ segments:
92
+ - 0
93
+ hash: -2264928236337400198
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ segments:
101
+ - 0
102
+ hash: -2264928236337400198
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 1.8.24
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: The Edinburgh Festivals API is an ambitious project which aims to create
109
+ open access to the event listings data of Edinburgh’s 12 major Festivals
110
+ test_files:
111
+ - test/lib/test_festivals_lab.rb
112
+ - test/test_helper.rb