dillo 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.
Files changed (3) hide show
  1. data/README.md +22 -0
  2. data/lib/dillo.rb +324 -0
  3. metadata +128 -0
@@ -0,0 +1,22 @@
1
+ #Dillo
2
+
3
+ Ruby library for working with the City of Austin [Data Portal](http://data.austintexas.gov). This library is derived from [Windy](https://github.com/Chicago/windy) which is a library for interacting with Chicago's [Socrata](http://www.socrata.com) powered data portal.
4
+
5
+ ##Installing
6
+
7
+ $ gem install dillo
8
+
9
+ ##Future Plans
10
+
11
+ + Full RDoc documentation
12
+ + Rake tasks for running tests and misc.
13
+ + More tests
14
+ + Example applications
15
+
16
+ ##Issues
17
+
18
+ Please use the issue tracker here on github.
19
+
20
+ ##Contributions
21
+
22
+ Any contributions are welcome, there is not an specific requirements or standards being used at the moment.
@@ -0,0 +1,324 @@
1
+ require 'faraday_middleware'
2
+ require 'multi_json'
3
+
4
+ module Dillo
5
+ VERSION = '0.1'
6
+
7
+ class << self
8
+ attr_accessor :app_token, :debug
9
+ end
10
+
11
+ module Response
12
+ class RaiseClientError < Faraday::Response::Middleware
13
+ def on_complete(env)
14
+ case env[:status].to_i
15
+ when 403
16
+ raise env[:response_headers][:x_error_message]
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ class SocrataAppTokenMiddleware < Faraday::Middleware
23
+ def call(env)
24
+ raise "You must specify an app token" if !Dillo.app_token
25
+ env[:request_headers]["X-App-Token"] = Dillo.app_token
26
+ @app.call env
27
+ end
28
+ end
29
+
30
+ class Base
31
+ def self.root
32
+ "http://data.austintexas.gov"
33
+ end
34
+
35
+ attr_reader :connection
36
+
37
+ def initialize
38
+ @connection = Faraday.new(:url => self.class.root) do |builder|
39
+ builder.use SocrataAppTokenMiddleware
40
+ builder.use Dillo::Response::RaiseClientError
41
+ builder.use FaradayMiddleware::ParseJson, :content_type => /\bjson$/
42
+
43
+ # Enable logger output with Dillojamie.phillips@austintexas.gov.debug = true
44
+ if Dillo.debug
45
+ builder.response :logger
46
+ end
47
+
48
+ builder.adapter :net_http
49
+ end
50
+ end
51
+
52
+ def body
53
+ @body ||= connection.get(path) do |request|
54
+ prepare_request(request)
55
+ # warn "Fetching #{request.to_env(@connection)[:url]}"
56
+ end.body
57
+ end
58
+
59
+ def prepare_request(request)
60
+ end
61
+
62
+ def json
63
+ # For some reason ruby 1.9.x seems to be trying to parse the
64
+ # API JSON output as ASCII instead of UTF-8
65
+ body.force_encoding("UTF-8") unless body.nil? || !body.respond_to?(:force_encoding)
66
+ @json ||= MultiJson.decode(body)
67
+ end
68
+
69
+ def inspect
70
+ "#<#{self.class.name} #{self.class.root}#{path}>"
71
+ end
72
+ end
73
+
74
+ module Finders
75
+ def [](index)
76
+ to_a[index]
77
+ end
78
+
79
+ def respond_to?(method)
80
+ method.to_s[/^find(_all)?_by_./] || super
81
+ end
82
+
83
+ def method_missing(method, *args, &block)
84
+ if attribute = method.to_s[/^(find(_all)?)_by_(.+)$/, 3]
85
+ value = args.first
86
+ send($1) { |record| record.send(attribute) == value }
87
+ else
88
+ super
89
+ end
90
+ end
91
+ end
92
+
93
+ class PaginatedCollection < Base
94
+ include Enumerable, Finders
95
+
96
+ def initialize
97
+ @pages ||= {}
98
+ end
99
+
100
+ def page(number)
101
+ @pages[number] ||= Page.new(self, number)
102
+ end
103
+
104
+ def each_page
105
+ number = 1
106
+ loop do
107
+ page = self.page(number)
108
+ break if page.count.zero?
109
+ yield page
110
+ number += 1
111
+ end
112
+ end
113
+
114
+ def each(&block)
115
+ each_page do |page|
116
+ page.each(&block)
117
+ end
118
+ end
119
+ end
120
+
121
+ class Collection < Base
122
+ include Enumerable, Finders
123
+
124
+ def each(&block)
125
+ records.each(&block)
126
+ end
127
+
128
+ def record_attributes
129
+ json
130
+ end
131
+
132
+ def records
133
+ @records ||= record_attributes.map do |attributes|
134
+ create_record(attributes)
135
+ end
136
+ end
137
+
138
+ def create_record(attributes)
139
+ record_class.new(attributes)
140
+ end
141
+ end
142
+
143
+ class Page < Collection
144
+ attr_accessor :collection, :number
145
+
146
+ def initialize(collection, number)
147
+ @collection = collection
148
+ @number = number
149
+ super()
150
+ end
151
+
152
+ def path
153
+ collection.path
154
+ end
155
+
156
+ def record_class
157
+ collection.record_class
158
+ end
159
+
160
+ def prepare_request(request)
161
+ request.params['limit'] = 200
162
+ request.params['page'] = @number
163
+ end
164
+ end
165
+
166
+ class Record
167
+ undef_method(:id) if method_defined?(:id)
168
+
169
+ attr_reader :attributes
170
+
171
+ def initialize(attributes)
172
+ self.attributes = attributes
173
+ end
174
+
175
+ def attributes=(attributes)
176
+ @attributes = Dillo.underscore(attributes)
177
+ end
178
+
179
+ def respond_to?(method)
180
+ @attributes.has_key?(method.to_s) || super
181
+ end
182
+
183
+ def method_missing(method, *args, &block)
184
+ if respond_to?(method)
185
+ @attributes[method.to_s]
186
+ else
187
+ super
188
+ end
189
+ end
190
+
191
+ def inspect
192
+ "#<#{self.class.name}:#{id}>"
193
+ end
194
+ end
195
+
196
+ class Views < PaginatedCollection
197
+ def path
198
+ "/api/views"
199
+ end
200
+
201
+ def record_class
202
+ View
203
+ end
204
+ end
205
+
206
+ class View < Record
207
+ def columns
208
+ @columns ||= Columns.new(id)
209
+ end
210
+
211
+ def rows
212
+ @rows ||= Rows.new(id)
213
+ end
214
+
215
+ def inspect
216
+ super[0..-2] + " #{name.inspect}>"
217
+ end
218
+ end
219
+
220
+ class Columns < Collection
221
+ def initialize(id)
222
+ @id = id
223
+ super()
224
+ end
225
+
226
+ def path
227
+ "/api/views/#{@id}/columns.json"
228
+ end
229
+
230
+ def record_class
231
+ Column
232
+ end
233
+ end
234
+
235
+ class Column < Record
236
+ def inspect
237
+ "#<#{self.class.name}:#{id} #{name.inspect}>"
238
+ end
239
+ end
240
+
241
+ class Rows < Collection
242
+ def initialize(id)
243
+ @id = id
244
+ super()
245
+ end
246
+
247
+ def path
248
+ "/api/views/#{@id}/rows.json"
249
+ end
250
+
251
+ def record_class
252
+ Row
253
+ end
254
+
255
+ def record_attributes
256
+ json['data']
257
+ end
258
+
259
+ def columns
260
+ @columns ||= json['meta']['view']['columns'].map { |column| column['name'].downcase }
261
+ end
262
+
263
+ def column_index(name)
264
+ columns.index(name.to_s.downcase)
265
+ end
266
+
267
+ def create_record(attributes)
268
+ record_class.new(self, attributes)
269
+ end
270
+ end
271
+
272
+ class Row
273
+ undef_method(:id) if method_defined?(:id)
274
+
275
+ attr_reader :collection, :values
276
+
277
+ def initialize(collection, values)
278
+ @collection = collection
279
+ @values = values
280
+ end
281
+
282
+ def [](index_or_name)
283
+ if index_or_name.is_a?(Integer)
284
+ values[index_or_name]
285
+ elsif index = collection.column_index(index_or_name)
286
+ values[index]
287
+ end
288
+ end
289
+
290
+ def method_missing(method, *args, &block)
291
+ name = method.to_s.gsub('_', ' ')
292
+ if collection.column_index(name)
293
+ self[name]
294
+ else
295
+ super
296
+ end
297
+ end
298
+
299
+ def length
300
+ values.length
301
+ end
302
+
303
+ def to_a
304
+ values
305
+ end
306
+
307
+ def inspect
308
+ "#<#{self.class.name} #{values.inspect}>"
309
+ end
310
+ end
311
+
312
+ def self.views
313
+ @views ||= Views.new
314
+ end
315
+
316
+ def self.underscore(hash)
317
+ Hash.new.tap do |result|
318
+ hash.each do |original_key, value|
319
+ new_key = original_key.gsub(/([a-z])([A-Z])/) { "#{$1}_#{$2.downcase}" }
320
+ result[new_key] = value.is_a?(Hash) ? underscore(value) : value
321
+ end
322
+ end
323
+ end
324
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dillo
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jamie Phillips
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faraday_middleware
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.8'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0.8'
30
+ - !ruby/object:Gem::Dependency
31
+ name: multi_json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.6'
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: '2.6'
62
+ - !ruby/object:Gem::Dependency
63
+ name: simplecov
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '0.4'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '0.4'
78
+ - !ruby/object:Gem::Dependency
79
+ name: webmock
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1.7'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.7'
94
+ description: Dillo is a Ruby module that allows you to easily interact with the City
95
+ of Austin's Data Portal.
96
+ email:
97
+ - jamie.phillips@austintexas.gov
98
+ executables: []
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - README.md
103
+ - lib/dillo.rb
104
+ homepage: http://github.com/phillipsj/dillo
105
+ licenses: []
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 1.8.24
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: Ruby interface to the City of Austin's Data Portal API
128
+ test_files: []