mocrata 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6ea5004761040d9a269f778003915705495aeb80
4
- data.tar.gz: 29f11a520caf96642656e6e0268b643f3d284cae
3
+ metadata.gz: fc529931b56e0de47b1a4ade8a83c05e4dc31b7e
4
+ data.tar.gz: c28c9d3d688b4129694fbf20c96d52168b1889e7
5
5
  SHA512:
6
- metadata.gz: d887cafd67ad5669aa6560c169eba34464db314df0763ebbc5dd697b22bf2b32d5f0312dc192d8323e9630cea1d1b03c420f33934fa08a855dab19792639820f
7
- data.tar.gz: 6ac06af00a4d45b148edd57adabf239f2d26e653f1ded892f9b36acd82ad99d94631858f3d7a6d6d20e6593880ff83120b1f27d2a917dcac8e5322b4169b1bc3
6
+ metadata.gz: 2a8e8142718c0b96126f2975e7108463667ce9a679b65fdab7ed1fe55c0c6f9915e5c53e088a5e1aab085c3f80cd3d2bda59c0819640e1f251b5a71c58093d9a
7
+ data.tar.gz: 2787a4131eabc7ee3fc12a612d03b4228fc2623a092f4d976bbbd971e2dee59ba6720f12659bc34d35ac064583c2fbbd51e31ae4551f5f10042b46dff10b6e10
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Mocrata
2
2
 
3
+ [![Build Status](https://travis-ci.org/mode/mocrata.svg?branch=master)](https://travis-ci.org/mode/mocrata)
4
+ [![Code Climate](https://codeclimate.com/repos/53d16a75695680764e01ea68/badges/c93756788e4438e90512/gpa.png)](https://codeclimate.com/repos/53d16a75695680764e01ea68/feed)
5
+ [![Gem Version](https://badge.fury.io/rb/mocrata.svg)](http://badge.fury.io/rb/mocrata)
6
+
3
7
  Mocrata is a [SODA](http://dev.socrata.com/) (Socrata Open Data API) client
4
8
  developed by [Mode Analytics](https://modeanalytics.com).
5
9
 
@@ -30,16 +34,29 @@ end
30
34
  ### Accessing data
31
35
 
32
36
  ```
33
- dataset = Mocrata::Dataset.new('http://soda.demo.socrata.com/resource/6xzm-fzcu')
37
+ dataset = Mocrata::Dataset.new("http://opendata.socrata.com/resource/mnkm-8ram")
38
+
39
+ dataset.name
40
+ => "Country List ISO 3166 Codes Latitude Longitude"
41
+
42
+ dataset.csv_header
43
+ => ["Country", "Alpha code", "Numeric code", "Latitude", "Longitude"]
34
44
 
35
45
  dataset.csv
36
- => [["Sally", 10], ["Earl", 2]]
46
+ => [["Albania", "AL", "8", "41", "20"],
47
+ ["Algeria", "DZ", "12", "28", "3"], ...]
37
48
 
38
49
  dataset.json
39
- => [{"name"=>"Sally", "age"=>10}, {"name"=>"Earl", "age"=>2}]
50
+ => [{"longitude_average"=>"20",
51
+ "latitude_average"=>"41",
52
+ "alpha_2_code"=>"AL",
53
+ "numeric_code"=>"8",
54
+ "country"=>"Albania"}, ...]
40
55
 
41
56
  dataset.fields
42
- => {"name"=>"text", "age"=>"number"}
57
+ => {":created_at"=>"meta_data", ":id"=>"meta_data", ":updated_at"=>"meta_data",
58
+ "alpha_2_code"=>"text", "country"=>"text", "latitude_average"=>"number",
59
+ "longitude_average"=>"number", "numeric_code"=>"number"}
43
60
  ```
44
61
 
45
62
  ### Iterating through rows
@@ -52,6 +69,10 @@ end
52
69
  dataset.each_row(:json) { |row| ... }
53
70
  ```
54
71
 
72
+ ## Documentation
73
+
74
+ http://rubydoc.info/github/mode/mocrata/master/frames
75
+
55
76
  ## Contributing
56
77
 
57
78
  1. Fork it
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/lib/mocrata.rb CHANGED
@@ -12,7 +12,8 @@ module Mocrata
12
12
  #
13
13
  CONTENT_TYPES = {
14
14
  :json => 'application/json',
15
- :csv => 'text/csv'
15
+ :csv => 'text/csv',
16
+ :xml => 'application/atom+xml'
16
17
  }
17
18
 
18
19
  class << self
@@ -7,20 +7,21 @@ module Mocrata
7
7
  class Dataset
8
8
  # Construct a new Dataset instance
9
9
  #
10
- # @param url [String] valid {http://dev.socrata.com SODA} resource url
10
+ # @param original_url [String] valid {http://dev.socrata.com SODA} resource
11
+ # url
11
12
  #
12
13
  # @return [Mocrata::Dataset] the instance
13
14
  #
14
15
  # @example
15
16
  # dataset = Mocrata::Dataset.new('http://data.sfgov.org/resource/funx-qxxn')
16
17
  #
17
- def initialize(url)
18
- @url = url
18
+ def initialize(original_url)
19
+ @original_url = original_url
19
20
  end
20
21
 
21
22
  # Iterate through each row of the dataset
22
23
  #
23
- # @param format [Symbol, String] the format, `:json` or `:csv`
24
+ # @param format [optional, Symbol] the format, `:json` or `:csv`
24
25
  #
25
26
  # @yield [Array<Array>] row of values
26
27
  #
@@ -29,7 +30,7 @@ module Mocrata
29
30
  # # do something with the row
30
31
  # end
31
32
  #
32
- def each_row(format, &block)
33
+ def each_row(format = :json, &block)
33
34
  each_page(format) do |page|
34
35
  page.each(&block)
35
36
  end
@@ -37,8 +38,12 @@ module Mocrata
37
38
 
38
39
  # Iterate through each page of the dataset
39
40
  #
40
- # @param format [Symbol, String] the format, `:json` or `:csv`
41
- # @param per_page [optional, Integer] the number of rows to return for each page
41
+ # @param format [optional, Symbol] the format, `:json` or `:csv`
42
+ # @param options [optional, Hash] hash of options
43
+ #
44
+ # @option options [Integer] :per_page the number of rows to return for each
45
+ # page
46
+ # @option options [Integer] :page the first page
42
47
  #
43
48
  # @yield [Array<Array>] page of rows
44
49
  #
@@ -47,56 +52,63 @@ module Mocrata
47
52
  # # do something with the page
48
53
  # end
49
54
  #
50
- def each_page(format, per_page = nil, &block)
51
- page = 1
52
- per_page ||= Mocrata.config.per_page
55
+ def each_page(format = :json, options = {}, &block)
56
+ page = options.fetch(:page, 1)
57
+ per_page = options.fetch(:per_page, Mocrata.config.per_page)
53
58
 
54
59
  while true
55
- rows = send(format, :page => page, :per_page => per_page)
60
+ rows = get(format, :page => page, :per_page => per_page).body
56
61
  yield rows
57
62
  break if rows.size < per_page
58
63
  page += 1
59
64
  end
60
65
  end
61
66
 
62
- # The contents of the dataset in CSV format
63
- #
64
- # @param params [optional, Hash] hash of options to pass along to the HTTP request
65
- #
66
- # @option params [Integer] :page the page to request
67
- # @option params [Integer] :per_page the number of rows to return for each page
67
+ # All rows in the dataset
68
68
  #
69
- # @return [Array<Array>] the array of rows
69
+ # @param format [optional, Symbol] the format, `:json` or `:csv`
70
70
  #
71
- # @example
72
- # dataset.csv(:page => 2, :per_page => 10)
71
+ # @return [Array] all rows in the requested format
73
72
  #
74
- def csv(params = {})
75
- get(:csv, params).body
73
+ def rows(format = :json)
74
+ rows = []
75
+ each_page(format) { |page| rows += page }
76
+ rows
76
77
  end
77
78
 
78
79
  # The contents of the dataset in JSON format
79
80
  #
80
- # @param params [optional, Hash] hash of options to pass along to the HTTP request
81
+ # @return [Array<Hash>] the array of rows
81
82
  #
82
- # @option params [Integer] :page the page to request
83
- # @option params [Integer] :per_page the number of rows to return for each page
83
+ def json
84
+ rows(:json)
85
+ end
86
+
87
+ # The contents of the dataset in CSV format
84
88
  #
85
- # @return [Array<Hash>] the array of rows
89
+ # @return [Array<Array>] the array of rows
86
90
  #
87
- # @example
88
- # dataset.json(:page => 2, :per_page => 10)
91
+ def csv
92
+ rows(:csv)
93
+ end
94
+
95
+ # The parsed header of the dataset in CSV format
89
96
  #
90
- def json(params = {})
91
- get(:json, params).body
97
+ # @return [Array<String>] the array of headers
98
+ #
99
+ def csv_header
100
+ options = { :paginate => false, :preserve_header => true }
101
+ params = { :limit => 0 }
102
+
103
+ Mocrata::Request.new(resource_url, :csv, options, params).response.body[0]
92
104
  end
93
105
 
94
- # Get the headers associated with the dataset
106
+ # Get the HTTP headers associated with the dataset (SODA doesn't support
107
+ # HEAD requests)
95
108
  #
96
109
  # @return [Hash] a hash of headers
97
110
  #
98
111
  def headers
99
- # SODA doesn't support HEAD requests, unfortunately
100
112
  @headers ||= get(:json, :per_page => 0).headers
101
113
  end
102
114
 
@@ -108,16 +120,40 @@ module Mocrata
108
120
  Hash[field_names.zip(field_types)]
109
121
  end
110
122
 
123
+ # A parsed representation of the dataset's {http://www.odata.org OData}
124
+ #
125
+ # @return [REXML::Document] a parsed REXML document
126
+ #
127
+ def odata
128
+ @odata ||= Mocrata::Request.new(odata_url, :xml, :top => 0).response.body
129
+ end
130
+
131
+ # The name of the dataset from OData
132
+ #
133
+ # @return [String] the dataset name
134
+ #
135
+ def name
136
+ odata.root.elements['title'].text
137
+ end
138
+
111
139
  private
112
140
 
113
- attr_reader :url
141
+ attr_reader :original_url
114
142
 
115
143
  def get(format, params = {})
116
- Mocrata::Request.new(base_url, format, params).response
144
+ Mocrata::Request.new(resource_url, format, params).response
145
+ end
146
+
147
+ def url
148
+ @url ||= Mocrata::DatasetUrl.new(original_url)
149
+ end
150
+
151
+ def resource_url
152
+ url.to_resource
117
153
  end
118
154
 
119
- def base_url
120
- @base_url ||= Mocrata::DatasetUrl.new(url).normalize
155
+ def odata_url
156
+ url.to_odata
121
157
  end
122
158
 
123
159
  def field_names
@@ -19,19 +19,20 @@ module Mocrata
19
19
  @original = original
20
20
  end
21
21
 
22
- # Normalize a Socrata dataset URL. Ensures https protocol. Removes query
23
- # string and fragment, if any.
22
+ # Convert the original URL to a normalized resource URL
24
23
  #
25
- # @return [String] the normalized URL
24
+ # @return [String] the resource URL
26
25
  #
27
- def normalize
28
- uri = URI(self.class.ensure_protocol(original))
29
-
30
- uri.scheme = 'https'
31
- uri.fragment = nil
32
- uri.query = nil
26
+ def to_resource
27
+ @to_resource ||= normalize
28
+ end
33
29
 
34
- self.class.strip_format(uri.to_s)
30
+ # Convert the original URL to a normalized OData URL
31
+ #
32
+ # @return [String] the OData URL
33
+ #
34
+ def to_odata
35
+ @to_odata ||= normalize.gsub(/\/resource\//, '/OData.svc/')
35
36
  end
36
37
 
37
38
  # Validate the original URL against the expected Socrata dataset URL
@@ -79,6 +80,21 @@ module Mocrata
79
80
 
80
81
  VALID_PATTERN = /\/resource\//
81
82
 
83
+ # Normalize a Socrata dataset URL. Ensures https protocol. Removes query
84
+ # string and fragment, if any.
85
+ #
86
+ # @return [String] the normalized URL
87
+ #
88
+ def normalize
89
+ uri = URI(self.class.ensure_protocol(original))
90
+
91
+ uri.scheme = 'https'
92
+ uri.fragment = nil
93
+ uri.query = nil
94
+
95
+ self.class.strip_format(uri.to_s)
96
+ end
97
+
82
98
  class InvalidError < StandardError; end
83
99
  end
84
100
  end
@@ -5,26 +5,38 @@ require 'csv'
5
5
  require 'json'
6
6
  require 'net/https'
7
7
 
8
+ require 'mocrata/version'
9
+
8
10
  module Mocrata
9
11
  # @attr_reader [String] url the request URL
10
12
  # @attr_reader [Symbol] format the request format, `:json` or `:csv`
11
- # @attr_reader [Hash] params the requst params
13
+ # @attr_reader [Hash] options hash of options
14
+ # @attr_reader [Hash] params the request params
12
15
  #
13
16
  class Request
14
- attr_reader :url, :format, :params
17
+ attr_reader :url, :format, :options, :params
15
18
 
16
19
  # Construct a new Request instance
17
20
  #
18
21
  # @param url [String] the request URL
19
22
  # @param format [Symbol] the request format, `:json` or `:csv`
20
- # @param params [Hash] the requst params
23
+ # @param options [optional, Hash] hash of options
24
+ # @param params [optional, Hash] the request params
25
+ #
26
+ # @option options [Integer] :page the page to request
27
+ # @option options [Integer] :per_page the number of rows to return for each
28
+ # page
29
+ # @option options [true, false] :paginate whether to add pagination params
30
+ # @option options [true, false] :preserve_header whether to preserve CSV
31
+ # header
21
32
  #
22
33
  # @return [Mocrata::Request] the instance
23
34
  #
24
- def initialize(url, format, params = {})
25
- @url = url
26
- @format = format
27
- @params = params
35
+ def initialize(url, format, options = {}, params = {})
36
+ @url = url
37
+ @format = format
38
+ @options = options
39
+ @params = params
28
40
  end
29
41
 
30
42
  # Perform the HTTP GET request
@@ -34,12 +46,14 @@ module Mocrata
34
46
  def response
35
47
  request = Net::HTTP::Get.new(uri.request_uri)
36
48
 
37
- request.add_field('Accept', content_type)
38
- request.add_field('X-App-Token', Mocrata.config.app_token)
49
+ request['accept'] = content_type
50
+ request['user-agent'] = USER_AGENT
51
+
52
+ request.add_field('x-app-token', Mocrata.config.app_token)
39
53
 
40
54
  response = http.request(request)
41
55
 
42
- Mocrata::Response.new(response).tap(&:validate!)
56
+ Mocrata::Response.new(response, response_options).tap(&:validate!)
43
57
  end
44
58
 
45
59
  # @return [String] the content type for the specified format
@@ -54,6 +68,9 @@ module Mocrata
54
68
 
55
69
  private
56
70
 
71
+ USER_AGENT = "mocrata/#{Mocrata::VERSION}"
72
+ SODA_PARAM_KEYS = %w(top limit)
73
+
57
74
  def http
58
75
  @http ||= Net::HTTP.new(uri.host, uri.port).tap do |http|
59
76
  http.use_ssl = true
@@ -63,16 +80,40 @@ module Mocrata
63
80
 
64
81
  def soda_params
65
82
  @soda_params ||= {}.tap do |soda|
66
- limit = params.fetch(:per_page, Mocrata.config.per_page)
67
- page = params.fetch(:page, 1)
83
+ soda.merge!(pagination_params) if paginate?
84
+
85
+ SODA_PARAM_KEYS.each do |key|
86
+ if params.has_key?(key.to_sym)
87
+ soda[:"$#{key}"] = params.fetch(key.to_sym)
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ def pagination_params
94
+ {}.tap do |result|
95
+ limit = options.fetch(:per_page, Mocrata.config.per_page)
96
+ page = options.fetch(:page, 1)
97
+
98
+ result[:$limit] = limit
99
+ result[:$offset] = (page - 1) * limit
100
+ end
101
+ end
102
+
103
+ def paginate?
104
+ options.fetch(:paginate, false) ||
105
+ options.has_key?(:page) ||
106
+ options.has_key?(:per_page)
107
+ end
68
108
 
69
- soda[:$limit] = limit
70
- soda[:$offset] = (page - 1) * limit
109
+ def response_options
110
+ options.keep_if do |key, value|
111
+ Mocrata::Response::OPTIONS.include?(key)
71
112
  end
72
113
  end
73
114
 
74
115
  def uri
75
- @uri ||= URI(url).dup.tap do |uri|
116
+ @uri ||= URI(url).tap do |uri|
76
117
  uri.query = self.class.query_string(soda_params)
77
118
  end
78
119
  end
@@ -1,18 +1,26 @@
1
1
  # encoding: utf-8
2
2
  #
3
- require 'json'
4
3
  require 'csv'
4
+ require 'json'
5
+ require 'rexml/document'
5
6
 
6
7
  module Mocrata
7
8
  class Response
9
+ OPTIONS = [:preserve_header]
10
+
8
11
  # Construct a new Response instance
9
12
  #
10
13
  # @param http_response [Net::HTTPResponse] the http response
14
+ # @param options [Hash] hash of options
15
+ #
16
+ # @option options [true, false] :preserve_header whether to preserve CSV
17
+ # header
11
18
  #
12
19
  # @return [Mocrata::Response] the instance
13
20
  #
14
- def initialize(http_response)
21
+ def initialize(http_response, options = {})
15
22
  @http_response = http_response
23
+ @options = options
16
24
  end
17
25
 
18
26
  # Perform certain checks against the HTTP response and raise an exception
@@ -29,6 +37,10 @@ module Mocrata
29
37
  end
30
38
  end
31
39
 
40
+ unless code == 200
41
+ raise ResponseError.new("Unexpected response code: #{code}")
42
+ end
43
+
32
44
  true
33
45
  end
34
46
 
@@ -46,6 +58,10 @@ module Mocrata
46
58
  end
47
59
  end
48
60
 
61
+ def code
62
+ http_response.code.to_i
63
+ end
64
+
49
65
  # The HTTP response body, processed according to content type
50
66
  #
51
67
  # @return [Array] the parsed body
@@ -59,7 +75,7 @@ module Mocrata
59
75
  # SODA headers that are always encoded as JSON
60
76
  JSON_HEADERS = %w(x-soda2-fields x-soda2-types)
61
77
 
62
- attr_reader :http_response
78
+ attr_reader :http_response, :options
63
79
 
64
80
  def content_type
65
81
  type = headers['content-type']
@@ -72,13 +88,23 @@ module Mocrata
72
88
  end
73
89
 
74
90
  def csv
75
- CSV.parse(http_response.body)[1..-1] # exclude header
91
+ result = CSV.parse(http_response.body)
92
+ result = result[1..-1] unless preserve_header?
93
+ result
76
94
  end
77
95
 
78
96
  def json
79
97
  JSON.parse(http_response.body)
80
98
  end
81
99
 
100
+ def xml
101
+ REXML::Document.new(http_response.body)
102
+ end
103
+
104
+ def preserve_header?
105
+ options.fetch(:preserve_header, false)
106
+ end
107
+
82
108
  class ResponseError < StandardError; end
83
109
  end
84
110
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
  #
3
3
  module Mocrata
4
- VERSION = "0.0.1"
4
+ VERSION = '0.1.0'
5
5
  end
data/mocrata.gemspec CHANGED
@@ -22,7 +22,6 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "rspec"
24
24
  spec.add_development_dependency "simplecov"
25
- spec.add_development_dependency "rdoc"
26
25
 
27
26
  spec.required_ruby_version = "~> 2.0"
28
27
  end
@@ -31,10 +31,12 @@ describe Mocrata::Dataset do
31
31
 
32
32
  describe '#each_page' do
33
33
  it 'yields pages' do
34
- expect(dataset).to receive(:json).and_return(*pages)
34
+ response = double(:response)
35
+ expect(response).to receive(:body).and_return(*pages)
36
+ expect(dataset).to receive(:get).and_return(response).at_least(:once)
35
37
 
36
38
  expect { |b|
37
- dataset.each_page(:json, 4, &b)
39
+ dataset.each_page(:json, :per_page => 4, &b)
38
40
  }.to yield_successive_args(*pages)
39
41
  end
40
42
  end
@@ -53,6 +55,38 @@ describe Mocrata::Dataset do
53
55
  end
54
56
  end
55
57
 
58
+ describe '#name' do
59
+ it 'fetches name from odata' do
60
+ dataset = Mocrata::Dataset.new(
61
+ 'https://data.sfgov.org/resource/dataset-identifier')
62
+
63
+ xml = %{<feed>
64
+ <title type="text">Test name</title>
65
+ <id>http://opendata.socrata.com/OData.svc/dataset-identifier</id>
66
+ <updated>2012-06-15T18:15:19Z</updated>
67
+ </feed>}
68
+
69
+ expect(dataset).to receive(:odata).and_return(REXML::Document.new(xml))
70
+ expect(dataset.name).to eq('Test name')
71
+ end
72
+ end
73
+
74
+ describe '#odata' do
75
+ it 'returns odata xml document' do
76
+ dataset = Mocrata::Dataset.new(
77
+ 'https://data.sfgov.org/resource/funx-qxxn')
78
+
79
+ response = Mocrata::Response.new(true)
80
+ expect(response).to receive(:content_type).and_return(:xml)
81
+ expect(response).to receive(:http_response).and_return(
82
+ double(:http_response, :body => ''))
83
+ expect_any_instance_of(Mocrata::Request).to receive(
84
+ :response).and_return(response)
85
+
86
+ expect(dataset.odata).to be_an_instance_of(REXML::Document)
87
+ end
88
+ end
89
+
56
90
  describe '#csv' do
57
91
  it 'returns csv body' do
58
92
  response = Mocrata::Response.new(true)
@@ -64,6 +98,20 @@ describe Mocrata::Dataset do
64
98
  end
65
99
  end
66
100
 
101
+ describe '#csv_header' do
102
+ it 'returns csv header' do
103
+ dataset = Mocrata::Dataset.new(
104
+ 'https://data.sfgov.org/resource/funx-qxxn')
105
+
106
+ response = double(:response, :body => [['foo', 'bar']])
107
+
108
+ expect_any_instance_of(Mocrata::Request).to receive(
109
+ :response).and_return(response)
110
+
111
+ expect(dataset.csv_header).to eq(['foo', 'bar'])
112
+ end
113
+ end
114
+
67
115
  describe '#json' do
68
116
  it 'returns json body' do
69
117
  response = Mocrata::Response.new(true)
@@ -25,7 +25,8 @@ describe Mocrata::DatasetUrl do
25
25
  url = Mocrata::DatasetUrl.new(
26
26
  'data.sfgov.org/resource/funx-qxxn.csv?limit=100#foo')
27
27
 
28
- expect(url.normalize).to eq('https://data.sfgov.org/resource/funx-qxxn')
28
+ expect(url.send(:normalize)).to eq(
29
+ 'https://data.sfgov.org/resource/funx-qxxn')
29
30
  end
30
31
  end
31
32
 
@@ -54,22 +54,97 @@ describe Mocrata::Request do
54
54
  end
55
55
 
56
56
  describe '#soda_params' do
57
- it 'is formed with default params' do
57
+ describe 'with pagination' do
58
+ it 'has default params' do
59
+ request = Mocrata::Request.new('', nil, :paginate => true)
60
+
61
+ result = request.send(:soda_params)
62
+
63
+ expect(result).to eq(:$limit => 1000, :$offset => 0)
64
+ end
65
+
66
+ it 'has custom params' do
67
+ request = Mocrata::Request.new('', nil, :paginate => true, :page => 2)
68
+
69
+ result = request.send(:soda_params)
70
+
71
+ expect(result).to eq(:$limit => 1000, :$offset => 1000)
72
+ end
73
+ end
74
+
75
+ describe 'without pagination' do
76
+ it 'is empty by default' do
77
+ request = Mocrata::Request.new('', nil)
78
+
79
+ expect(request.send(:soda_params)).to eq({})
80
+ end
81
+ end
82
+
83
+ it 'ignores unrecognized parameters' do
84
+ request = Mocrata::Request.new('', nil, {}, { :foo => 'bar', :top => 0 })
85
+
86
+ expect(request.send(:soda_params)).to eq(:$top => 0)
87
+ end
88
+ end
89
+
90
+ describe '#pagination_params' do
91
+ it 'is formed with default pagination options' do
92
+ request = Mocrata::Request.new('', nil, :paginate => true)
93
+
94
+ result = request.send(:pagination_params)
95
+
96
+ expect(result).to eq(:$limit => 1000, :$offset => 0)
97
+ end
98
+
99
+ it 'is formed with custom pagination options' do
100
+ request = Mocrata::Request.new('', nil,
101
+ :page => 5, :per_page => 100)
102
+
103
+ result = request.send(:pagination_params)
104
+
105
+ expect(result).to eq(:$limit => 100, :$offset => 400)
106
+ end
107
+ end
108
+
109
+ describe '#paginate?' do
110
+ it 'is false by default' do
58
111
  request = Mocrata::Request.new('', nil)
59
112
 
60
- expect(request.send(:soda_params)).to eq(:$limit => 1000, :$offset => 0)
113
+ expect(request.send(:paginate?)).to eq(false)
61
114
  end
62
115
 
63
- it 'is formed with custom params' do
64
- request = Mocrata::Request.new('', nil, :page => 5, :per_page => 100)
116
+ it 'allows override' do
117
+ request = Mocrata::Request.new('', nil, :paginate => true)
65
118
 
66
- expect(request.send(:soda_params)).to eq(:$limit => 100, :$offset => 400)
119
+ expect(request.send(:paginate?)).to eq(true)
67
120
  end
68
121
 
69
- it 'ignores custom params' do
70
- request = Mocrata::Request.new('', nil, :wat => 'nope')
122
+ it 'is true if page option is present' do
123
+ request = Mocrata::Request.new('', nil, :page => 1)
71
124
 
72
- expect(request.send(:soda_params)).to eq(:$limit => 1000, :$offset => 0)
125
+ expect(request.send(:paginate?)).to eq(true)
126
+ end
127
+
128
+ it 'is true if per_page option is present' do
129
+ request = Mocrata::Request.new('', nil, :per_page => 1)
130
+
131
+ expect(request.send(:paginate?)).to eq(true)
132
+ end
133
+ end
134
+
135
+ describe '#response_options' do
136
+ it 'is empty by default' do
137
+ request = Mocrata::Request.new('', nil)
138
+
139
+ expect(request.send(:response_options)).to eq({})
140
+ end
141
+
142
+ it 'filters request options' do
143
+ request = Mocrata::Request.new('', nil,
144
+ :page => 1,
145
+ :preserve_header => true)
146
+
147
+ expect(request.send(:response_options)).to eq(:preserve_header => true)
73
148
  end
74
149
  end
75
150
 
@@ -80,7 +155,7 @@ describe Mocrata::Request do
80
155
 
81
156
  it 'is formed with default parameters' do
82
157
  request = Mocrata::Request.new(
83
- 'https://data.sfgov.org/resource/funx-qxxn', nil)
158
+ 'https://data.sfgov.org/resource/funx-qxxn', nil, :paginate => true)
84
159
 
85
160
  result = request.send(:uri).to_s
86
161
 
@@ -92,7 +167,8 @@ describe Mocrata::Request do
92
167
  request = Mocrata::Request.new(
93
168
  'https://data.sfgov.org/resource/funx-qxxn', nil,
94
169
  :per_page => 5,
95
- :page => 3)
170
+ :page => 3,
171
+ :paginate => true)
96
172
 
97
173
  result = request.send(:uri).to_s
98
174
 
@@ -9,27 +9,40 @@ describe Mocrata::Response do
9
9
 
10
10
  describe '#validate!' do
11
11
  it 'returns true without content type' do
12
+ expect(response).to receive(:code).and_return(200)
12
13
  expect(response).to receive(:content_type).and_return(nil)
13
14
  expect(response.validate!).to be true
14
15
  end
15
16
 
16
17
  it 'returns true with csv content' do
18
+ expect(response).to receive(:code).and_return(200)
17
19
  expect(response).to receive(:content_type).and_return(:csv)
18
20
  expect(response.validate!).to be true
19
21
  end
20
22
 
21
23
  it 'returns true with json array' do
24
+ expect(response).to receive(:code).and_return(200)
22
25
  expect(response).to receive(:content_type).and_return(:json)
23
26
  expect(response).to receive(:body).and_return([])
24
27
  expect(response.validate!).to be true
25
28
  end
26
29
 
27
30
  it 'returns true with json array' do
31
+ expect(response).to receive(:code).and_return(200)
28
32
  expect(response).to receive(:content_type).and_return(:json)
29
33
  expect(response).to receive(:body).at_least(:once).and_return({})
30
34
  expect(response.validate!).to be true
31
35
  end
32
36
 
37
+ it 'raises exception if response code is unexpected' do
38
+ expect(response).to receive(:code).and_return(201).at_least(:once)
39
+ expect(response).to receive(:content_type).and_return(:json)
40
+ expect(response).to receive(:body).at_least(:once).and_return({})
41
+
42
+ expect { response.validate! }.to raise_error(
43
+ Mocrata::Response::ResponseError)
44
+ end
45
+
33
46
  it 'raises exception with json error' do
34
47
  expect(response).to receive(:content_type).and_return(:json)
35
48
  expect(response).to receive(:body).at_least(:once).and_return(
@@ -79,10 +92,21 @@ describe Mocrata::Response do
79
92
  end
80
93
 
81
94
  describe '#csv' do
82
- it 'parses body and excludes header' do
83
- csv = "\"header1\"\n\"row1\"\n\"row2\""
84
- expect(response.send(:http_response)).to receive(:body).and_return(csv)
85
- expect(response.send(:csv)).to eq([['row1'], ['row2']])
95
+ describe 'without header' do
96
+ it 'parses body and excludes header' do
97
+ csv = "\"header1\"\n\"row1\"\n\"row2\""
98
+ expect(response.send(:http_response)).to receive(:body).and_return(csv)
99
+ expect(response.send(:csv)).to eq([['row1'], ['row2']])
100
+ end
101
+ end
102
+
103
+ describe 'with header' do
104
+ it 'parses body and preserves header' do
105
+ expect(response).to receive(:preserve_header?).and_return(true)
106
+ csv = "\"header1\"\n\"row1\"\n\"row2\""
107
+ expect(response.send(:http_response)).to receive(:body).and_return(csv)
108
+ expect(response.send(:csv)).to eq([['header1'], ['row1'], ['row2']])
109
+ end
86
110
  end
87
111
  end
88
112
 
@@ -129,4 +153,27 @@ describe Mocrata::Response do
129
153
  'x-soda2-fields' => { 'name' => 'value' })
130
154
  end
131
155
  end
156
+
157
+ describe '#code' do
158
+ it 'converts http response status to integer' do
159
+ http_response = double(:http_response, :code => '200')
160
+ response = Mocrata::Response.new(http_response)
161
+
162
+ expect(response.code).to eq(200)
163
+ end
164
+ end
165
+
166
+ describe '#preserve_header?' do
167
+ it 'is false by default' do
168
+ response = Mocrata::Response.new(true)
169
+
170
+ expect(response.send(:preserve_header?)).to eq(false)
171
+ end
172
+
173
+ it 'allows override' do
174
+ response = Mocrata::Response.new(true, :preserve_header => true)
175
+
176
+ expect(response.send(:preserve_header?)).to eq(true)
177
+ end
178
+ end
132
179
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mocrata
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Heather Rivers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-24 00:00:00.000000000 Z
11
+ date: 2014-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: rdoc
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - '>='
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - '>='
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
69
  description: Mode's SODA client
84
70
  email:
85
71
  - heather@modeanalytics.com
@@ -88,6 +74,7 @@ extensions: []
88
74
  extra_rdoc_files: []
89
75
  files:
90
76
  - .gitignore
77
+ - .travis.yml
91
78
  - .yardopts
92
79
  - Gemfile
93
80
  - LICENSE.txt