pricehubble 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +30 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +62 -0
  6. data/.simplecov +3 -0
  7. data/.travis.yml +27 -0
  8. data/.yardopts +6 -0
  9. data/Appraisals +25 -0
  10. data/CHANGELOG.md +9 -0
  11. data/Dockerfile +29 -0
  12. data/Envfile +6 -0
  13. data/Gemfile +8 -0
  14. data/LICENSE +21 -0
  15. data/Makefile +149 -0
  16. data/README.md +385 -0
  17. data/Rakefile +80 -0
  18. data/bin/console +16 -0
  19. data/bin/run +12 -0
  20. data/bin/setup +8 -0
  21. data/config/docker/.bash_profile +3 -0
  22. data/config/docker/.bashrc +48 -0
  23. data/config/docker/.inputrc +17 -0
  24. data/doc/assets/project.svg +68 -0
  25. data/doc/examples/authentication.rb +19 -0
  26. data/doc/examples/complex_property_valuations.rb +91 -0
  27. data/doc/examples/config.rb +30 -0
  28. data/doc/examples/property_valuations_errors.rb +30 -0
  29. data/doc/examples/simple_property_valuations.rb +69 -0
  30. data/docker-compose.yml +9 -0
  31. data/gemfiles/rails_4.2.gemfile +11 -0
  32. data/gemfiles/rails_5.0.gemfile +11 -0
  33. data/gemfiles/rails_5.1.gemfile +11 -0
  34. data/gemfiles/rails_5.2.gemfile +11 -0
  35. data/lib/price_hubble.rb +3 -0
  36. data/lib/pricehubble/client/authentication.rb +29 -0
  37. data/lib/pricehubble/client/base.rb +59 -0
  38. data/lib/pricehubble/client/request/data_sanitization.rb +28 -0
  39. data/lib/pricehubble/client/request/default_headers.rb +33 -0
  40. data/lib/pricehubble/client/response/data_sanitization.rb +29 -0
  41. data/lib/pricehubble/client/response/recursive_open_struct.rb +31 -0
  42. data/lib/pricehubble/client/utils/request.rb +41 -0
  43. data/lib/pricehubble/client/utils/response.rb +60 -0
  44. data/lib/pricehubble/client/valuation.rb +68 -0
  45. data/lib/pricehubble/client.rb +25 -0
  46. data/lib/pricehubble/configuration.rb +26 -0
  47. data/lib/pricehubble/configuration_handling.rb +50 -0
  48. data/lib/pricehubble/core_ext/hash.rb +52 -0
  49. data/lib/pricehubble/entity/address.rb +11 -0
  50. data/lib/pricehubble/entity/authentication.rb +34 -0
  51. data/lib/pricehubble/entity/base_entity.rb +63 -0
  52. data/lib/pricehubble/entity/concern/associations.rb +197 -0
  53. data/lib/pricehubble/entity/concern/attributes/date_array.rb +29 -0
  54. data/lib/pricehubble/entity/concern/attributes/enum.rb +57 -0
  55. data/lib/pricehubble/entity/concern/attributes/range.rb +32 -0
  56. data/lib/pricehubble/entity/concern/attributes/string_inquirer.rb +27 -0
  57. data/lib/pricehubble/entity/concern/attributes.rb +171 -0
  58. data/lib/pricehubble/entity/concern/callbacks.rb +19 -0
  59. data/lib/pricehubble/entity/concern/client.rb +31 -0
  60. data/lib/pricehubble/entity/coordinates.rb +11 -0
  61. data/lib/pricehubble/entity/location.rb +14 -0
  62. data/lib/pricehubble/entity/property.rb +32 -0
  63. data/lib/pricehubble/entity/property_conditions.rb +20 -0
  64. data/lib/pricehubble/entity/property_qualities.rb +20 -0
  65. data/lib/pricehubble/entity/property_type.rb +21 -0
  66. data/lib/pricehubble/entity/valuation.rb +48 -0
  67. data/lib/pricehubble/entity/valuation_request.rb +60 -0
  68. data/lib/pricehubble/entity/valuation_scores.rb +11 -0
  69. data/lib/pricehubble/errors.rb +60 -0
  70. data/lib/pricehubble/faraday.rb +12 -0
  71. data/lib/pricehubble/identity.rb +46 -0
  72. data/lib/pricehubble/railtie.rb +16 -0
  73. data/lib/pricehubble/utils/bangers.rb +44 -0
  74. data/lib/pricehubble/utils/decision.rb +97 -0
  75. data/lib/pricehubble/version.rb +6 -0
  76. data/lib/pricehubble.rb +103 -0
  77. data/pricehubble.gemspec +47 -0
  78. metadata +432 -0
data/README.md ADDED
@@ -0,0 +1,385 @@
1
+ ![PriceHubble](doc/assets/project.svg)
2
+
3
+ [![Build Status](https://travis-ci.com/hausgold/pricehubble.svg?token=4XcyqxxmkyBSSV3wWRt7&branch=master)](https://travis-ci.com/hausgold/pricehubble)
4
+ [![Gem Version](https://badge.fury.io/rb/pricehubble.svg)](https://badge.fury.io/rb/pricehubble)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/cd15f59fc84566e4b200/maintainability)](https://codeclimate.com/repos/5da572bd60163201b800c255/maintainability)
6
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/cd15f59fc84566e4b200/test_coverage)](https://codeclimate.com/repos/5da572bd60163201b800c255/test_coverage)
7
+ [![API docs](https://img.shields.io/badge/docs-API-blue.svg)](https://www.rubydoc.info/gems/pricehubble)
8
+
9
+ This project is dedicated to build a client/API wrapper around the
10
+ [PriceHubble](https://pricehubble.com) REST API. It follows strictly the
11
+ [version 1 of the API specification](https://docs.pricehubble.com). Furthermore
12
+ this gem allows you to easily interact with common PriceHubble operations like
13
+ fetching property valuations. At the time of writing it supports not the full
14
+ API specification, but it should be easy to enhance the client functionality,
15
+ so feel free to send a pull request.
16
+
17
+ - [Installation](#installation)
18
+ - [Usage](#usage)
19
+ - [Configuration](#configuration)
20
+ - [Available environment variables](#available-environment-variables)
21
+ - [Authentication](#authentication)
22
+ - [Error Handling](#error-handling)
23
+ - [Property Valuations](#property-valuations)
24
+ - [Bare Minimum Request Example](#bare-minimum-request-example)
25
+ - [Use the PriceHubble::Valuation representation](#use-the-pricehubblevaluation-representation)
26
+ - [Error Handling](#error-handling-1)
27
+ - [Advanced Request Examples](#advanced-request-examples)
28
+ - [Development](#development)
29
+ - [Contributing](#contributing)
30
+
31
+ ## Installation
32
+
33
+ Add this line to your application's Gemfile:
34
+
35
+ ```ruby
36
+ gem 'pricehubble'
37
+ ```
38
+
39
+ And then execute:
40
+
41
+ ```bash
42
+ $ bundle
43
+ ```
44
+
45
+ Or install it yourself as:
46
+
47
+ ```bash
48
+ $ gem install pricehubble
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ### Configuration
54
+
55
+ You can configure the pricehubble gem via an Rails initializer, by environment
56
+ variables or on demand. Here we show a common Rails initializer example:
57
+
58
+ ```ruby
59
+ PriceHubble.configure do |conf|
60
+ # Configure the API authentication credentials
61
+ conf.username = 'your-username'
62
+ conf.password = 'your-password'
63
+
64
+ # The base URL of the API (there is no staging/canary
65
+ # endpoint we know about)
66
+ conf.base_url = 'https://api.pricehubble.com'
67
+
68
+ # Writes to stdout by default
69
+ conf.logger = Logger.new(IO::NULL)
70
+ end
71
+ ```
72
+
73
+ The pricehubble gem comes with sensitive defaults as you can see. For most
74
+ users an extra configuration, beside the authorization credentials, is not
75
+ needed.
76
+
77
+ #### Available environment variables
78
+
79
+ The pricehubble gem can be configured hardly with its configuration code block
80
+ like shown before. Respecting the [twelve-factor app](https://12factor.net/)
81
+ concerns, the gem allows you to set almost all configurations (just the
82
+ relevant ones for runtime) via environment variables. Here comes a list of
83
+ available configuration options:
84
+
85
+ * **PRICEHUBBLE_USERNAME**: The API username to use for authentication.
86
+ * **PRICEHUBBLE_PASSWORD**: The API password to use for authentication.
87
+ * **PRICEHUBBLE_BASE_URL**: The base URL of the API. Defaults to the production one.
88
+
89
+ ### Authentication
90
+
91
+ After configuring the pricehubble gem you can directly fetch an
92
+ authentication which is valid for two hours. All operational requests
93
+ require an authentication to be present and unexpired. But now comes
94
+ the good part: you don't ever need to take care about this, because
95
+ the gem handles the refreshing of the authentication transparently for
96
+ you and also takes care of passing the authentication to each request
97
+ you can do with the gem.
98
+
99
+ ```ruby
100
+ # Fetch the authentication/identity for the first time, subsequent calls
101
+ # to this method will return the cached authentication instance until it
102
+ # is expired (or near expiration, 5 minutes leeway) then a new
103
+ # authentication is fetched transparently
104
+ PriceHubble.identity
105
+ # => #<PriceHubble::Authentication access_token="...", ...>
106
+
107
+ # Check the expiration state (transparently always unexpired)
108
+ PriceHubble.identity.expired?
109
+ # => false
110
+
111
+ # Get the current authentication expiration date/time
112
+ PriceHubble.identity.expires_at
113
+ # => 2019-10-17 08:01:23 +0000
114
+ ```
115
+
116
+ ### Error Handling
117
+
118
+ The pricehubble gem allows you to decide how to react on errors on a
119
+ per-request basis. (except the transparent authentication) All request
120
+ performing methods are shiped in a bang and non-bang variant.
121
+
122
+ The bang variants (eg. `PriceHubble::ValuationRequest#perform!`, mind
123
+ the exclamation mark at the end) will raise an child instance of the
124
+ `PriceHubble::RequestError` or `PriceHubble::EntityError` class ([see
125
+ errors for more details](lib/pricehubble/errors.rb)) when errors
126
+ occur. This comes in handy on asynchronous jobs which are retried on
127
+ exceptions.
128
+
129
+ The non-bang variants (eg. `PriceHubble::ValuationRequest#perform`,
130
+ without the exclamation mark) wont raise and just return empty results
131
+ (eg. `false` or `[]`). This might me comfortable in complex control
132
+ flows or when you do not care if one out of 100 times the data is
133
+ missing. But watch out for bad/invalid requests you might mask with
134
+ this behaviour.
135
+
136
+ It's up to you to choose the correct flavor for your usecase.
137
+
138
+ ### Property Valuations
139
+
140
+ The pricehubble gem allows you to request property valuations easily.
141
+ You can formulate your request elegantly with in-place attribute sets
142
+ or already built instances the same way. Furthermore the gem ships
143
+ some sensible defaults which makes it even more easy to get an
144
+ valuation for a property.
145
+
146
+ The `PriceHubble::ValuationRequest` allows to fetch valuations for one
147
+ or more (bulk) properties at once for one or several dates. The upper
148
+ limit for this is `properties.count * dates.count <= 50` from API
149
+ side.
150
+
151
+ **Gotcha!** The API allows to fetch property valuations with
152
+ per-property independent time series. The pricehubble gem does not
153
+ support this for the sake of ease.
154
+
155
+ #### Bare Minimum Request Example
156
+
157
+ ```ruby
158
+ # Fetch the valuations for a single property (sale) for today (defaults).
159
+ # This is the bare minimum variant, as a starting point.
160
+ valuations = PriceHubble::ValuationRequest.new(
161
+ property: {
162
+ location: {
163
+ address: {
164
+ post_code: '22769',
165
+ city: 'Hamburg',
166
+ street: 'Stresemannstr.',
167
+ house_number: '29'
168
+ }
169
+ },
170
+ property_type: { code: :apartment },
171
+ building_year: 1990,
172
+ living_area: 200
173
+ }
174
+ ).perform!
175
+ # => [#<PriceHubble::Valuation ...>]
176
+
177
+ # Print all relevant valuation attributes
178
+ pp valuations.first.attributes.deep_compact
179
+ # => {"currency"=>"EUR",
180
+ # => "sale_price"=>1283400,
181
+ # => "sale_price_range"=>1180800..1386100,
182
+ # => "confidence"=>"good",
183
+ # => "deal_type"=>"sale",
184
+ # => "valuation_date"=>Thu, 17 Oct 2019,
185
+ # => "country_code"=>"DE",
186
+ # => "property"=>
187
+ # => {"location"=>
188
+ # => {"address"=>
189
+ # => {"post_code"=>"22769",
190
+ # => "city"=>"Hamburg",
191
+ # => "street"=>"Stresemannstr.",
192
+ # => "house_number"=>"29"}},
193
+ # => "property_type"=>{"code"=>:apartment},
194
+ # => "building_year"=>1990,
195
+ # => "living_area"=>200}}
196
+ ```
197
+
198
+ #### Use the PriceHubble::Valuation representation
199
+
200
+ The [`PriceHubble::Valuation`](lib/pricehubble/entity/valuation.rb)
201
+ representation ships a lot utilities and helpers to process the data.
202
+ Here are some examples:
203
+
204
+ ```ruby
205
+ # Fetch the valuations
206
+ valuations = PriceHubble::ValuationRequest.new(...).perform!
207
+ # We just want to work on the first valuation
208
+ valuation = valuations.first
209
+
210
+ # Get the deal type dependent value of the property in a generic way.
211
+ # (sale price if deal type is sale, and rent gross if deal type is rent)
212
+ valuation.value
213
+ # => 1283400
214
+
215
+ # Get the upper and lower value range of the property in a generic
216
+ # way. The deal type logic is equal to the
217
+ # +PriceHubble::Valuation#value+ method.
218
+ valuation.value_range
219
+ # => 1180800..1386100
220
+
221
+ # Query the valuation confidence in an elegant way
222
+ valuation.confidence.good?
223
+ # => true
224
+
225
+ # The +PriceHubble::Valuation+ entity is a self contained
226
+ # representation of the request and response. This means it includes
227
+ # the property, deal type, valuation date, country code, etc it was
228
+ # requested for. This makes it easy to process the data because
229
+ # everything related is in one place.
230
+ valuation.property.property_type.apartment?
231
+ # => true
232
+ ```
233
+
234
+ #### Error Handling
235
+
236
+ The property valuation API is able to perform bulk operations which
237
+ may result in non-bang situations, even when you use the bang variant.
238
+ This is quite smart as a single property valuation might not break
239
+ others on the very same request. Watch for the
240
+ `PriceHubble::Valuation#status` hash, when it is not nil, then you got
241
+ an error for this valuation.
242
+
243
+ In case you request just one property valuation, then the API will
244
+ respond an error which is mapped when you use the bang variants.
245
+
246
+ ```ruby
247
+ begin
248
+ # Fetch the valuations for a single property (sale) for today (defaults).
249
+ # This is the bare minimum variant, as a starting point.
250
+ PriceHubble::ValuationRequest.new(
251
+ property: {
252
+ location: {
253
+ address: {
254
+ post_code: '22769',
255
+ city: 'Hamburg',
256
+ street: 'Stresemannstr.',
257
+ house_number: '29'
258
+ }
259
+ },
260
+ property_type: { code: :apartment },
261
+ building_year: 2999,
262
+ living_area: 200
263
+ }
264
+ ).perform!
265
+ rescue PriceHubble::EntityInvalid => e
266
+ # => #<PriceHubble::EntityInvalid: buildingYear: ...>
267
+
268
+ # The error message includes the detailed problem.
269
+ e.message
270
+ # => "buildingYear: Must be between 1850 and 2022."
271
+ end
272
+ ```
273
+
274
+ #### Advanced Request Examples
275
+
276
+ Here comes a more complex example of a property valuations request
277
+ with multiple properties, for several valuations dates.
278
+
279
+ **Gotcha!** The API does not allow you to mix and match the deal type
280
+ per property. Therefore you need to set it once for the whole request.
281
+
282
+ ```ruby
283
+ # Build a property with all relevant information for the valuation. This
284
+ # example reflects not the full list of supported fields, [see the API
285
+ # specification](https://docs.pricehubble.com/#types-property) for the full
286
+ # details.
287
+ apartment = PriceHubble::Property.new(
288
+ location: {
289
+ address: {
290
+ post_code: '22769',
291
+ city: 'Hamburg',
292
+ street: 'Stresemannstr.',
293
+ house_number: '29'
294
+ }
295
+ },
296
+ property_type: { code: :apartment },
297
+ building_year: 1990,
298
+ living_area: 200,
299
+ balcony_Area: 30,
300
+ floor_number: 5,
301
+ has_lift: true,
302
+ is_furnished: false,
303
+ is_new: false,
304
+ renovation_year: 2014,
305
+ condition: {
306
+ bathrooms: :well_maintained,
307
+ kitchen: :well_maintained,
308
+ flooring: :well_maintained,
309
+ windows: :well_maintained,
310
+ masonry: :well_maintained
311
+ },
312
+ quality: {
313
+ bathrooms: :normal,
314
+ kitchen: :normal,
315
+ flooring: :normal,
316
+ windows: :normal,
317
+ masonry: :normal
318
+ }
319
+ )
320
+
321
+ house = PriceHubble::Property.new(
322
+ location: {
323
+ address: {
324
+ post_code: '22769',
325
+ city: 'Hamburg',
326
+ street: 'Stresemannstr.',
327
+ house_number: '29'
328
+ }
329
+ },
330
+ property_type: { code: :house },
331
+ building_year: 1990,
332
+ land_area: 100,
333
+ living_area: 500,
334
+ number_of_floors_in_building: 5
335
+ )
336
+
337
+ # Fetch the property valuations for multiple properties, on multiple dates
338
+ request = PriceHubble::ValuationRequest.new(
339
+ deal_type: :sale,
340
+ properties: [apartment, house],
341
+ # The dates order is reflected on the valuations list
342
+ valuation_dates: [
343
+ 1.year.ago,
344
+ Date.current,
345
+ 1.year.from_now
346
+ ]
347
+ )
348
+ valuations = request.perform!
349
+
350
+ # Print the valuations in a simple ASCII table. This is just a
351
+ # demonstration of how to use the valuations for a simple usecase.
352
+ require 'terminal-table'
353
+ table = Terminal::Table.new do |tab|
354
+ tab << ['Deal Type', 'Property Type', *request.valuation_dates.map(&:year)]
355
+ tab << :separator
356
+ # Group the valuations by the property they represent
357
+ valuations.group_by(&:property).each do |property, valuations|
358
+ tab << [request.deal_type, property.property_type.code,
359
+ *valuations.map { |val| "#{val.value} #{val.currency}" }]
360
+ end
361
+ end
362
+ # => +-----------+---------------+-------------+-------------+-------------+
363
+ # => | Deal Type | Property Type | 2018 | 2019 | 2020 |
364
+ # => +-----------+---------------+-------------+-------------+-------------+
365
+ # => | sale | apartment | 1282100 EUR | 1373100 EUR | 1420100 EUR |
366
+ # => | sale | house | 1824800 EUR | 1950900 EUR | 2016000 EUR |
367
+ # => +-----------+---------------+-------------+-------------+-------------+
368
+ ```
369
+
370
+ ## Development
371
+
372
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
373
+ `bundle exec rake spec` to run the tests. You can also run `bin/console` for an
374
+ interactive prompt that will allow you to experiment.
375
+
376
+ To install this gem onto your local machine, run `bundle exec rake install`. To
377
+ release a new version, update the version number in `version.rb`, and then run
378
+ `bundle exec rake release`, which will create a git tag for the version, push
379
+ git commits and tags, and push the `.gem` file to
380
+ [rubygems.org](https://rubygems.org).
381
+
382
+ ## Contributing
383
+
384
+ Bug reports and pull requests are welcome on GitHub at
385
+ https://github.com/hausgold/pricehubble.
data/Rakefile ADDED
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rails/code_statistics'
6
+ require 'pp'
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task default: :spec
11
+
12
+ # Load some railties tasks
13
+ load 'rails/tasks/statistics.rake'
14
+ load 'rails/tasks/annotations.rake'
15
+
16
+ # Clear the default statistics directory constant
17
+ #
18
+ # rubocop:disable Style/MutableConstant because we define it
19
+ Object.send(:remove_const, :STATS_DIRECTORIES)
20
+ ::STATS_DIRECTORIES = []
21
+ # rubocop:enable Style/MutableConstant
22
+
23
+ # Monkey patch the Rails +CodeStatistics+ class to support configurable
24
+ # patterns per path. This is reuqired to support top-level only file matches.
25
+ class CodeStatistics
26
+ DEFAULT_PATTERN = /^(?!\.).*?\.(rb|js|coffee|rake)$/.freeze
27
+
28
+ # Pass the possible +pattern+ argument down to the
29
+ # +calculate_directory_statistics+ method call.
30
+ def calculate_statistics
31
+ Hash[@pairs.map do |pair|
32
+ [pair.first, calculate_directory_statistics(*pair[1..-1])]
33
+ end]
34
+ end
35
+
36
+ # Match the pattern against the individual file name and the relative file
37
+ # path. This allows top-level only matches.
38
+ def calculate_directory_statistics(directory, pattern = DEFAULT_PATTERN)
39
+ stats = CodeStatisticsCalculator.new
40
+
41
+ Dir.foreach(directory) do |file_name|
42
+ path = "#{directory}/#{file_name}"
43
+
44
+ if File.directory?(path) && (/^\./ !~ file_name)
45
+ stats.add(calculate_directory_statistics(path, pattern))
46
+ elsif file_name =~ pattern || path =~ pattern
47
+ stats.add_by_file_path(path)
48
+ end
49
+ end
50
+
51
+ stats
52
+ end
53
+ end
54
+
55
+ # Configure all code statistics directories
56
+ vendors = [
57
+ [:unshift, 'Clients', 'lib/pricehubble/client'],
58
+ [:unshift, 'Entities', 'lib/pricehubble/entity'],
59
+ [:unshift, 'Utilities', 'lib/pricehubble/utils'],
60
+ [:unshift, 'Top-levels', 'lib', %r{lib(/pricehubble)?/[^/]+\.rb$}],
61
+
62
+ [:unshift, 'Clients specs', 'spec/client'],
63
+ [:unshift, 'Entities specs', 'spec/entity'],
64
+ [:unshift, 'Utilities specs', 'spec/utils'],
65
+ [:unshift, 'Top-levels specs', 'spec',
66
+ %r{spec/pricehubble(_spec\.rb|/[^/]+\.rb$)}]
67
+ ].reverse
68
+
69
+ vendors.each do |method, type, dir, pattern|
70
+ ::STATS_DIRECTORIES.send(method, [type, dir, pattern].compact)
71
+ ::CodeStatistics::TEST_TYPES << type if type.include? 'specs'
72
+ end
73
+
74
+ # Setup annotations
75
+ ENV['SOURCE_ANNOTATION_DIRECTORIES'] = 'spec,doc'
76
+
77
+ desc 'Enumerate all annotations'
78
+ task :notes do
79
+ SourceAnnotationExtractor.enumerate '@?OPTIMIZE|@?FIXME|@?TODO', tag: true
80
+ end
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'price-hubble'
6
+ require 'pp'
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+
11
+ # (If you use this, don't forget to add pry to your Gemfile!)
12
+ # require "pry"
13
+ # Pry.start
14
+
15
+ require 'irb'
16
+ IRB.start(__FILE__)
data/bin/run ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'price-hubble'
6
+ require 'pp'
7
+
8
+ $stdout.sync = true
9
+
10
+ exit 1 if ARGV.empty?
11
+
12
+ ARGV.each { |cmd| eval(cmd); $stdout.flush }
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ if [ -f ~/.bashrc ]; then
2
+ . ~/.bashrc
3
+ fi
@@ -0,0 +1,48 @@
1
+ # ~/.bashrc: executed by bash(1) for non-login shells.
2
+ # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
3
+ # for examples
4
+
5
+ _GEM_PATHS=$(ls -d1 ${HOME}/.gem/ruby/*/bin 2>/dev/null | paste -sd ':')
6
+ _APP_PATHS=$(ls -d1 /app/vendor/bundle/ruby/*/bin 2>/dev/null | paste -sd ':')
7
+
8
+ export PATH="${_GEM_PATHS}:${_APP_PATHS}:${PATH}"
9
+ export PATH="/app/node_modules/.bin:${HOME}/.bin:/app/bin:${PATH}"
10
+ export MAKE_ENV=baremetal
11
+
12
+ # Disable the autostart of all supervisord units
13
+ sudo sed -i 's/autostart=.*/autostart=false/g' /etc/supervisor/conf.d/*
14
+
15
+ # Start the supervisord (empty, no units)
16
+ sudo supervisord >/dev/null 2>&1 &
17
+
18
+ # Wait for supervisord
19
+ while ! supervisorctl status >/dev/null 2>&1; do sleep 1; done
20
+
21
+ # Boot the mDNS stack
22
+ echo '# Start the mDNS stack'
23
+ sudo supervisorctl start dbus avahi
24
+ echo
25
+
26
+ function watch-make-test()
27
+ {
28
+ while [ 1 ]; do
29
+ inotifywait --quiet -r `pwd` -e close_write --format '%e -> %w%f'
30
+ make test
31
+ done
32
+ }
33
+
34
+ function watch-make()
35
+ {
36
+ while [ 1 ]; do
37
+ inotifywait --quiet -r `pwd` -e close_write --format '%e -> %w%f'
38
+ make $@
39
+ done
40
+ }
41
+
42
+ function watch-run()
43
+ {
44
+ while [ 1 ]; do
45
+ inotifywait --quiet -r `pwd` -e close_write --format '%e -> %w%f'
46
+ bash -c "$@"
47
+ done
48
+ }
@@ -0,0 +1,17 @@
1
+ # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving
2
+ "\e[1;5C": forward-word
3
+ "\e[1;5D": backward-word
4
+ "\e[5C": forward-word
5
+ "\e[5D": backward-word
6
+ "\e\e[C": forward-word
7
+ "\e\e[D": backward-word
8
+
9
+ # handle common Home/End escape codes
10
+ "\e[1~": beginning-of-line
11
+ "\e[4~": end-of-line
12
+ "\e[7~": beginning-of-line
13
+ "\e[8~": end-of-line
14
+ "\eOH": beginning-of-line
15
+ "\eOF": end-of-line
16
+ "\e[H": beginning-of-line
17
+ "\e[F": end-of-line
@@ -0,0 +1,68 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
4
+ xmlns:cc="http://creativecommons.org/ns#"
5
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6
+ xmlns:svg="http://www.w3.org/2000/svg"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ version="1.1"
9
+ id="Ebene_1"
10
+ x="0px"
11
+ y="0px"
12
+ viewBox="0 0 800 200"
13
+ xml:space="preserve"
14
+ width="800"
15
+ height="200"><metadata
16
+ id="metadata33"><rdf:RDF><cc:Work
17
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
18
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
19
+ id="defs31" />
20
+ <style
21
+ type="text/css"
22
+ id="style2">
23
+ .st0{fill-rule:evenodd;clip-rule:evenodd;fill:#E73E11;}
24
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#0371B9;}
25
+ .st2{fill:#132E48;}
26
+ .st3{font-family:'OpenSans-Bold';}
27
+ .st4{font-size:29.5168px;}
28
+ .st5{fill-rule:evenodd;clip-rule:evenodd;fill:none;}
29
+ .st6{opacity:0.5;fill:#132E48;}
30
+ .st7{font-family:'OpenSans';}
31
+ .st8{font-size:12px;}
32
+ </style>
33
+ <g
34
+ transform="translate(0,1.53584)"
35
+ id="g828"><g
36
+ transform="translate(35.93985,35.66416)"
37
+ id="g8">
38
+ <path
39
+ style="clip-rule:evenodd;fill:#e73e11;fill-rule:evenodd"
40
+ id="path4"
41
+ d="m -0.1,124.4 c 0,0 33.7,-123.2 66.7,-123.2 12.8,0 26.9,21.9 38.8,47.2 -23.6,27.9 -66.6,59.7 -94,76 -7.1,0 -11.5,0 -11.5,0 z"
42
+ class="st0" />
43
+ <path
44
+ style="clip-rule:evenodd;fill:#0371b9;fill-rule:evenodd"
45
+ id="path6"
46
+ d="m 88.1,101.8 c 13.5,-10.4 18.4,-16.2 27.1,-25.4 10,25.7 16.7,48 16.7,48 0,0 -41.4,0 -78,0 14.6,-7.9 18.7,-10.7 34.2,-22.6 z"
47
+ class="st1" />
48
+ </g><text
49
+ y="106.40316"
50
+ x="192.43155"
51
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:29.51733398px;font-family:'Open Sans', sans-serif;-inkscape-font-specification:'OpenSans-Bold, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#132e48"
52
+ id="text10"
53
+ class="st2 st3 st4">PriceHubble</text>
54
+ <rect
55
+ style="clip-rule:evenodd;fill:none;fill-rule:evenodd"
56
+ id="rect12"
57
+ height="24"
58
+ width="314.5"
59
+ class="st5"
60
+ y="118.06416"
61
+ x="194.23985" /><text
62
+ y="127.22146"
63
+ x="194.21715"
64
+ style="font-size:12px;font-family:'Open Sans', sans-serif;opacity:0.5;fill:#132e48;-inkscape-font-specification:'Open Sans, sans-serif, Normal';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;"
65
+ id="text14"
66
+ class="st6 st7 st8">Ruby client for the PriceHubble REST API</text>
67
+ </g>
68
+ </svg>
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative './config'
5
+
6
+ # Fetch the authentication/identity for the first time, subsequent calls
7
+ # to this method will return the cached authentication instance until it
8
+ # is expired (or near expiration, 5 minutes leeway) then a new
9
+ # authentication is fetched transparently
10
+ pp PriceHubble.identity
11
+ # => #<PriceHubble::Authentication access_token="...", ...>
12
+
13
+ # Check the expiration state (transparently always unexpired)
14
+ pp PriceHubble.identity.expired?
15
+ # => false
16
+
17
+ # Get the current authentication expiration date/time
18
+ pp PriceHubble.identity.expires_at
19
+ # => 2019-10-17 08:01:23 +0000