pricehubble 0.2.0 → 0.4.2

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
  SHA256:
3
- metadata.gz: 708dc6002a8c92bc8cdcebbfd1d580c72d199644f343a650d19d9229dd90e86c
4
- data.tar.gz: 59ea57131a818d6fa7af30743963c9e991b167a9e0d364bae6727dc86d91cd72
3
+ metadata.gz: 6c029e790cdf1ec21949212567077f03271666ca6332a3b84a902a262ae6625e
4
+ data.tar.gz: 3df77cae40f611c456283a23e215a840ce88936748aefb9c729a205c61ebd7d8
5
5
  SHA512:
6
- metadata.gz: b416c6035d36d6266ab6968bd445feb5f964d1af09d9feff852cd998f34d5054ae33962e878fc6d1128ef6e19557154da22372a157f3e492a6bb37b936717dbe
7
- data.tar.gz: 3511f1a0bdad3da7ce4f58214e59cc34b159b8012441df368193bff5b162ff6d799a49c5a0fbd6abd65c09f875c2ea1e7000d9c1a67c85a4fd034f5e3d28dfe0
6
+ metadata.gz: 6421682c63aa011271eca9931587badf8fbf7ad1c64aadbfa9bccef42dd170e68dd3dee5ed9f8e965ee47e2055e0fdca398ed355370bd91c8a28e045fbc26729
7
+ data.tar.gz: ebe7f75db3e959c48074e0c74092ca6a9a94789645aba5051ab1a972fb8be66295092fe1a07f62a680f53c977152724e8aef31ae06e32afc316b47092e46c66f
@@ -0,0 +1,38 @@
1
+ name: Build Documentation
2
+ on:
3
+ repository_dispatch:
4
+ types: [documentation]
5
+
6
+ concurrency:
7
+ group: 'docs'
8
+
9
+ jobs:
10
+ docs:
11
+ name: Build gem documentation
12
+ runs-on: ubuntu-20.04
13
+ timeout-minutes: 5
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+
17
+ - name: Install the correct Ruby version
18
+ uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: 2.5
21
+ bundler-cache: true
22
+
23
+ - name: Prepare the virtual environment
24
+ uses: hausgold/actions/ci@master
25
+ with:
26
+ clone_token: '${{ secrets.CLONE_TOKEN }}'
27
+ settings: '${{ github.repository }}'
28
+ target: ci/gem-test
29
+
30
+ - name: Build gem documentation
31
+ run: make docs
32
+
33
+ - name: Upload the code coverage report
34
+ run: coverage
35
+
36
+ - name: Add this job to the commit status
37
+ run: commit-status '${{ job.status }}'
38
+ if: always()
@@ -0,0 +1,62 @@
1
+ name: Test
2
+ on:
3
+ push:
4
+ branches:
5
+ - '**'
6
+ schedule:
7
+ - cron: '0 0 * * MON'
8
+
9
+ concurrency:
10
+ group: '${{ github.ref }}'
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ test:
15
+ name: 'Test the gem (Ruby ${{ matrix.ruby }}, Rails ${{ matrix.rails }})'
16
+ runs-on: ubuntu-20.04
17
+ timeout-minutes: 5
18
+ strategy:
19
+ fail-fast: false
20
+ matrix:
21
+ ruby: [2.5, 2.6, 2.7]
22
+ rails: [5.2, '6.0']
23
+ env:
24
+ BUNDLE_GEMFILE: 'gemfiles/rails_${{ matrix.rails }}.gemfile'
25
+ steps:
26
+ - uses: actions/checkout@v2
27
+
28
+ - name: Install the correct Ruby version
29
+ uses: ruby/setup-ruby@v1
30
+ with:
31
+ ruby-version: ${{ matrix.ruby }}
32
+ bundler-cache: true
33
+
34
+ - name: Prepare the virtual environment
35
+ uses: hausgold/actions/ci@master
36
+ with:
37
+ clone_token: '${{ secrets.CLONE_TOKEN }}'
38
+ settings: '${{ github.repository }}'
39
+ target: ci/gem-test
40
+
41
+ - name: Run the gem tests
42
+ run: make test
43
+
44
+ - name: Upload the code coverage report
45
+ run: coverage
46
+
47
+ trigger-docs:
48
+ name: Trigger the documentation upload
49
+ runs-on: ubuntu-20.04
50
+ timeout-minutes: 2
51
+ needs: test
52
+ if: github.ref == 'refs/heads/master'
53
+ steps:
54
+ - name: Prepare the virtual environment
55
+ uses: hausgold/actions/ci@master
56
+ with:
57
+ clone_token: '${{ secrets.CLONE_TOKEN }}'
58
+ settings: '${{ github.repository }}'
59
+ target: ci/noop
60
+
61
+ - name: Trigger the documentation upload
62
+ run: workflow documentation
data/.rubocop.yml CHANGED
@@ -8,7 +8,7 @@ Documentation:
8
8
 
9
9
  AllCops:
10
10
  DisplayCopNames: true
11
- TargetRubyVersion: 2.3
11
+ TargetRubyVersion: 2.5
12
12
  Exclude:
13
13
  - bin/**/*
14
14
  - vendor/**/*
@@ -60,3 +60,8 @@ Rails/SkipsModelValidations:
60
60
  Lint/ShadowingOuterLocalVariable:
61
61
  Exclude:
62
62
  - "doc/**/*"
63
+
64
+ # We stay at 80 characters per line.
65
+ # See: https://rubystyle.guide/#max-line-length
66
+ Metrics/LineLength:
67
+ Max: 80
data/Appraisals CHANGED
@@ -1,25 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- appraise 'rails-4.2' do
4
- gem 'activemodel', '~> 4.2.11'
5
- gem 'activesupport', '~> 4.2.11'
6
- gem 'railties', '~> 4.2.11'
7
- end
8
-
9
- appraise 'rails-5.0' do
10
- gem 'activemodel', '~> 5.0.7'
11
- gem 'activesupport', '~> 5.0.7'
12
- gem 'railties', '~> 5.0.7'
13
- end
14
-
15
- appraise 'rails-5.1' do
16
- gem 'activemodel', '~> 5.1.6'
17
- gem 'activesupport', '~> 5.1.6'
18
- gem 'railties', '~> 5.1.6'
19
- end
20
-
21
3
  appraise 'rails-5.2' do
22
4
  gem 'activemodel', '~> 5.2.2'
23
5
  gem 'activesupport', '~> 5.2.2'
24
6
  gem 'railties', '~> 5.2.2'
25
7
  end
8
+
9
+ appraise 'rails-6.0' do
10
+ gem 'activemodel', '~> 6.0.0'
11
+ gem 'activesupport', '~> 6.0.0'
12
+ gem 'railties', '~> 6.0.0'
13
+ end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ### 0.4.2
2
+
3
+ * Migrated to Github Actions
4
+ * Migrated to our own coverage reporting
5
+ * Added the code statistics to the test process
6
+
7
+ ### 0.4.1
8
+
9
+ * Corrected the GNU Make release target
10
+
11
+ ### 0.4.0
12
+
13
+ * Added initial dossier handling (create, delete, sharing link) (#3)
14
+
15
+ ### 0.3.0
16
+
17
+ * Dropped support for Rails <5.2 (#2)
18
+ * Dropped support for Ruby <2.5 (#2)
19
+ * Updated the faraday gem spec to `~> 1.0` (#2)
20
+
1
21
  ### 0.2.0
2
22
 
3
23
  * Added a configuration for request logging which is enabled by default now
data/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM hausgold/ruby:2.3
1
+ FROM hausgold/ruby:2.5
2
2
  MAINTAINER Hermann Mayer <hermann.mayer@hausgold.de>
3
3
 
4
4
  # Update system gem
data/Makefile CHANGED
@@ -28,9 +28,9 @@ XARGS ?= xargs
28
28
  # Container binaries
29
29
  BUNDLE ?= bundle
30
30
  APPRAISAL ?= appraisal
31
+ GEM ?= gem
31
32
  RAKE ?= rake
32
33
  YARD ?= yard
33
- RAKE ?= rake
34
34
  RUBOCOP ?= rubocop
35
35
 
36
36
  # Files
@@ -75,16 +75,18 @@ install:
75
75
  # Install the dependencies
76
76
  @$(MKDIR) -p $(VENDOR_DIR)
77
77
  @$(call run-shell,$(BUNDLE) check || $(BUNDLE) install --path $(VENDOR_DIR))
78
+ @$(call run-shell,GEM_HOME=vendor/bundle/ruby/$${RUBY_MAJOR}.0 \
79
+ $(GEM) install bundler -v "~> 1.0")
78
80
  @$(call run-shell,$(BUNDLE) exec $(APPRAISAL) install)
79
81
 
80
- update: install
82
+ update:
81
83
  # Install the dependencies
82
84
  @$(MKDIR) -p $(VENDOR_DIR)
83
85
  @$(call run-shell,$(BUNDLE) exec $(APPRAISAL) update)
84
86
 
85
- test: #install
87
+ test:
86
88
  # Run the whole test suite
87
- @$(call run-shell,$(BUNDLE) exec $(RAKE))
89
+ @$(call run-shell,$(BUNDLE) exec $(RAKE) stats spec)
88
90
 
89
91
  $(TEST_GEMFILES): GEMFILE=$(@:test-%=%)
90
92
  $(TEST_GEMFILES):
@@ -116,34 +118,34 @@ endif
116
118
  clean-images:
117
119
  # Clean build images
118
120
  ifeq ($(MAKE_ENV),docker)
119
- @-$(DOCKER) images | $(GREP) price-hubble \
121
+ @-$(DOCKER) images | $(GREP) pricehubble \
120
122
  | $(AWK) '{ print $$3 }' \
121
123
  | $(XARGS) -rn1 $(DOCKER) rmi -f
122
124
  endif
123
125
 
124
126
  distclean: clean clean-containers clean-images
125
127
 
126
- shell: install
128
+ shell:
127
129
  # Run an interactive shell on the container
128
130
  @$(call run-shell,$(BASH) -i)
129
131
 
130
- shell-irb: install
132
+ shell-irb:
131
133
  # Run an interactive IRB shell on the container
132
134
  @$(call run-shell,bin/console)
133
135
 
134
- docs: install
136
+ docs:
135
137
  # Build the API documentation
136
138
  @$(call run-shell,$(BUNDLE) exec $(YARD) -q && \
137
139
  $(BUNDLE) exec $(YARD) stats --list-undoc --compact)
138
140
 
139
- notes: install
141
+ notes:
140
142
  # Print the code statistics (library and test suite)
141
143
  @$(call run-shell,$(BUNDLE) exec $(RAKE) notes)
142
144
 
143
- stats: install
145
+ stats:
144
146
  # Print all the notes from the code
145
147
  @$(call run-shell,$(BUNDLE) exec $(RAKE) stats)
146
148
 
147
149
  release:
148
150
  # Release a new gem version
149
- @$(RAKE) release
151
+ @$(BUNDLE) exec $(RAKE) release
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  ![PriceHubble](doc/assets/project.svg)
2
2
 
3
- [![Build Status](https://travis-ci.com/hausgold/pricehubble.svg?token=4XcyqxxmkyBSSV3wWRt7&branch=master)](https://travis-ci.com/hausgold/pricehubble)
3
+ [![Continuous Integration](https://github.com/hausgold/pricehubble/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/hausgold/pricehubble/actions/workflows/test.yml)
4
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)
5
+ [![Test Coverage](https://automate-api.hausgold.de/v1/coverage_reports/pricehubble/coverage.svg)](https://knowledge.hausgold.de/coverage)
6
+ [![Test Ratio](https://automate-api.hausgold.de/v1/coverage_reports/pricehubble/ratio.svg)](https://knowledge.hausgold.de/coverage)
7
+ [![API docs](https://automate-api.hausgold.de/v1/coverage_reports/pricehubble/documentation.svg)](https://www.rubydoc.info/gems/pricehubble)
8
8
 
9
9
  This project is dedicated to build a client/API wrapper around the
10
10
  [PriceHubble](https://pricehubble.com) REST API. It follows strictly the
@@ -25,6 +25,7 @@ so feel free to send a pull request.
25
25
  - [Use the PriceHubble::Valuation representation](#use-the-pricehubblevaluation-representation)
26
26
  - [Error Handling](#error-handling-1)
27
27
  - [Advanced Request Examples](#advanced-request-examples)
28
+ - [Dossiers](#dossiers)
28
29
  - [Development](#development)
29
30
  - [Contributing](#contributing)
30
31
 
@@ -370,6 +371,56 @@ end
370
371
  # => +-----------+---------------+-------------+-------------+-------------+
371
372
  ```
372
373
 
374
+ ### Dossiers
375
+
376
+ The pricehubble gem allows you to create and delete dossiers for properties.
377
+ The required property data is equal to the valuation request. Additionally the
378
+ pricehubble gem allows you to create sharing links (permalinks) which will link
379
+ to the dossier dashboard application for customers.
380
+
381
+ **Gotcha!** The API allows to update and search for dossiers. Additionally the
382
+ API supports images and logos for dossiers. The pricehubble gem does not
383
+ support this yet.
384
+
385
+ ```ruby
386
+ # The property to create a dossier for
387
+ apartment = {
388
+ location: {
389
+ address: {
390
+ post_code: '22769',
391
+ city: 'Hamburg',
392
+ street: 'Stresemannstr.',
393
+ house_number: '29'
394
+ }
395
+ },
396
+ property_type: { code: :apartment },
397
+ building_year: 1990,
398
+ living_area: 200
399
+ }
400
+
401
+ dossier = PriceHubble::Dossier.new(
402
+ title: 'Customer Dossier for Stresemannstr. 29',
403
+ description: 'Best apartment in the city',
404
+ deal_type: :sale,
405
+ property: apartment,
406
+ country_code: 'DE',
407
+ asking_sale_price: 600_000 # the minimum price the seller is willing to agree
408
+ # valuation_override_sale_price: '', # overwrite the PH value
409
+ # valuation_override_rent_net: '', # overwrite the PH value
410
+ # valuation_override_rent_gross: '', # overwrite the PH value
411
+ # valuation_override_reason_freetext: '' # explain the visitor why
412
+ )
413
+
414
+ # Save the new dossier
415
+ pp dossier.save!
416
+ pp dossier.id
417
+ # => "25de5429-244e-4584-b58e-b0d7428a2377"
418
+
419
+ # Generate a sharing link for the dossier
420
+ pp dossier.link
421
+ # => "https://dash.pricehubble.com/shared/dossier/eyJ0eXAiOiJ..."
422
+ ```
423
+
373
424
  ## Development
374
425
 
375
426
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
data/Rakefile CHANGED
@@ -54,16 +54,21 @@ end
54
54
 
55
55
  # Configure all code statistics directories
56
56
  vendors = [
57
- [:unshift, 'Clients', 'lib/pricehubble/client'],
58
- [:unshift, 'Entities', 'lib/pricehubble/entity'],
59
- [:unshift, 'Utilities', 'lib/pricehubble/utils'],
60
57
  [:unshift, 'Top-levels', 'lib', %r{lib(/pricehubble)?/[^/]+\.rb$}],
58
+ [:unshift, 'Top-levels specs', 'spec',
59
+ %r{spec/pricehubble(_spec\.rb|/[^/]+\.rb$)}],
61
60
 
61
+ [:unshift, 'Clients', 'lib/pricehubble/client'],
62
62
  [:unshift, 'Clients specs', 'spec/client'],
63
+
64
+ [:unshift, 'Entities', 'lib/pricehubble/entity'],
63
65
  [:unshift, 'Entities specs', 'spec/entity'],
66
+
67
+ [:unshift, 'Utilities', 'lib/pricehubble/utils'],
64
68
  [:unshift, 'Utilities specs', 'spec/utils'],
65
- [:unshift, 'Top-levels specs', 'spec',
66
- %r{spec/pricehubble(_spec\.rb|/[^/]+\.rb$)}]
69
+
70
+ [:unshift, 'Instrumentation', 'lib/pricehubble/instrumentation'],
71
+ [:unshift, 'Core Extensions', 'lib/pricehubble/core_ext']
67
72
  ].reverse
68
73
 
69
74
  vendors.each do |method, type, dir, pattern|
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative './config'
5
+
6
+ # The property to create a dossier for
7
+ apartment = {
8
+ location: {
9
+ address: {
10
+ post_code: '22769',
11
+ city: 'Hamburg',
12
+ street: 'Stresemannstr.',
13
+ house_number: '29'
14
+ }
15
+ },
16
+ property_type: { code: :apartment },
17
+ building_year: 1990,
18
+ living_area: 200
19
+ }
20
+
21
+ dossier = PriceHubble::Dossier.new(
22
+ title: 'Customer Dossier for Stresemannstr. 29',
23
+ description: 'Best apartment in the city',
24
+ deal_type: :sale,
25
+ property: apartment,
26
+ country_code: 'DE',
27
+ asking_sale_price: 600_000 # the minimum price the seller is willing to agree
28
+ # valuation_override_sale_price: '', # overwrite the PH value
29
+ # valuation_override_rent_net: '', # overwrite the PH value
30
+ # valuation_override_rent_gross: '', # overwrite the PH value
31
+ # valuation_override_reason_freetext: '' # explain the visitor why
32
+ )
33
+
34
+ # Save the new dossier
35
+ pp dossier.save!
36
+ pp dossier.id
37
+ # => "25de5429-244e-4584-b58e-b0d7428a2377"
38
+
39
+ # Generate a sharing link for the dossier
40
+ pp dossier.link
41
+ # => "https://dash.pricehubble.com/shared/dossier/eyJ0eXAiOiJ..."
@@ -4,8 +4,8 @@
4
4
 
5
5
  source 'https://rubygems.org'
6
6
 
7
- gem 'activemodel', '~> 5.0.7'
8
- gem 'activesupport', '~> 5.0.7'
9
- gem 'railties', '~> 5.0.7'
7
+ gem 'activemodel', '~> 6.0.0'
8
+ gem 'activesupport', '~> 6.0.0'
9
+ gem 'railties', '~> 6.0.0'
10
10
 
11
11
  gemspec path: '../'
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PriceHubble
4
+ module Client
5
+ # A high level client library for the PriceHubble Dossiers API.
6
+ class Dossiers < Base
7
+ # Create a new dossier.
8
+ #
9
+ # @param entity [PriceHubble::Dossier] the entity to use
10
+ # @param args [Hash{Symbol => Mixed}] additional arguments
11
+ # @return [PriceHubble::Dossier, nil] the PriceHubble dossier,
12
+ # or +nil+ on error
13
+ #
14
+ # rubocop:disable Metrics/MethodLength because thats the bare minimum
15
+ # handling is quite complex
16
+ def create_dossier(entity, **args)
17
+ res = connection.post do |req|
18
+ req.path = '/api/v1/dossiers'
19
+ req.body = entity.attributes.compact
20
+ use_default_context(req, :create_dossier)
21
+ use_authentication(req)
22
+ end
23
+ decision(bang: args.fetch(:bang, false)) do |result|
24
+ result.bang(&bang_entity(entity, res, {}))
25
+ result.good(&assign_entity(entity, res))
26
+ successful?(res)
27
+ end
28
+ end
29
+ # rubocop:enable Metrics/MethodLength
30
+
31
+ # Generates a permalink for the specified dossier which will expire after
32
+ # the set number of days.
33
+ #
34
+ # @param entity [PriceHubble::Dossier] the entity to use
35
+ # @param ttl [ActiveSupport::Duration] the time to live for the new link
36
+ # @param locale [String] the user frontend locale
37
+ # @param args [Hash{Symbol => Mixed}] additional arguments
38
+ #
39
+ # rubocop:disable Metrics/MethodLength because thats the bare minimum
40
+ # rubocop:disable Metrics/AbcSize because the decission
41
+ # handling is quite complex
42
+ def share_dossier(entity, ttl:, locale:, **args)
43
+ res = connection.post do |req|
44
+ req.path = '/api/v1/dossiers/links'
45
+ req.body = {
46
+ dossier_id: entity.id,
47
+ days_to_live: ttl.fdiv(1.day.to_i).ceil,
48
+ country_code: entity.country_code,
49
+ locale: locale
50
+ }
51
+ use_default_context(req, :share_dossier)
52
+ use_authentication(req)
53
+ end
54
+ decision(bang: args.fetch(:bang, false)) do |result|
55
+ result.bang(&bang_entity(entity, res, id: entity.try(:id)))
56
+ result.good { res.body.url }
57
+ successful?(res)
58
+ end
59
+ end
60
+ # rubocop:enable Metrics/MethodLength
61
+ # rubocop:enable Metrics/AbcSize
62
+
63
+ # Delete a dossier entity.
64
+ #
65
+ # @param entity [PriceHubble::Dossier] the entity to delete
66
+ # @param args [Hash{Symbol => Mixed}] additional arguments
67
+ #
68
+ # rubocop:disable Metrics/MethodLength because thats the bare minimum
69
+ # rubocop:disable Metrics/AbcSize because the decission
70
+ # handling is quite complex
71
+ def delete_dossier(entity, **args)
72
+ res = connection.delete do |req|
73
+ req.path = "/api/v1/dossiers/#{entity.id}"
74
+ use_default_context(req, :delete_dossier)
75
+ use_authentication(req)
76
+ end
77
+ decision(bang: args.fetch(:bang, false)) do |result|
78
+ result.bang(&bang_entity(entity, res, id: entity.id))
79
+ result.good(&assign_entity(entity, res) do |assigned_entity|
80
+ assigned_entity.mark_as_destroyed.freeze
81
+ end)
82
+ successful?(res)
83
+ end
84
+ end
85
+ # rubocop:enable Metrics/MethodLength
86
+ # rubocop:enable Metrics/AbcSize
87
+
88
+ # Update a dossier entity.
89
+ #
90
+ # TODO: Implement this.
91
+ #
92
+ # @param entity [PriceHubble::Dossier] the entity to update
93
+ # @param args [Hash{Symbol => Mixed}] additional arguments
94
+ # @return [PriceHubble::Dossier, nil] the entity, or +nil+ on error
95
+ def update_dossier(*)
96
+ # PUT dossiers/<dossier_id>
97
+ raise NotImplementedError
98
+ end
99
+
100
+ # Search for dossier entities.
101
+ #
102
+ # TODO: Implement this.
103
+ #
104
+ # @param criteria [Mixed] the search criteria
105
+ # @param args [Hash{Symbol => Mixed}] additional arguments
106
+ # @return [Array<PriceHubble::Dossier>, nil] the entity,
107
+ # or +nil+ on error
108
+ def search_dossiers(*)
109
+ # POST dossiers/search
110
+ raise NotImplementedError
111
+ end
112
+
113
+ # Generate bang method variants
114
+ bangers :create_dossier, :update_dossier, :delete_dossier,
115
+ :share_dossier, :search_dossiers
116
+ end
117
+ end
118
+ end
@@ -18,7 +18,7 @@ module PriceHubble
18
18
 
19
19
  # By definition empty responses (HTTP status 204)
20
20
  # or actual empty bodies should be an empty hash
21
- body = {} if res[:status] == 204 || res[:body].empty?
21
+ body = {} if res[:status] == 204 || res[:body].blank?
22
22
 
23
23
  # Looks like we have some actual data we can wrap
24
24
  res[:body] = \
@@ -4,6 +4,8 @@ module PriceHubble
4
4
  module Client
5
5
  module Utils
6
6
  # Some helpers to work with responses in a general way.
7
+ #
8
+ # rubocop:disable Metrics/BlockLength because of ActiveSupport::Concern
7
9
  module Response
8
10
  extend ActiveSupport::Concern
9
11
 
@@ -53,8 +55,33 @@ module PriceHubble
53
55
  PriceHubble::RequestError.new(nil, res)
54
56
  end
55
57
  end
58
+
59
+ # Perform the assignment of the response to the given entity. This
60
+ # allows a clean usage of the decision flow control for successful
61
+ # requests. Here comes an example:
62
+ #
63
+ # decision do |result|
64
+ # result.good(&assign_entity(entity, res))
65
+ # end
66
+ #
67
+ # @param entity [Hausgold::BaseEntity] the entity instance to handle
68
+ # @param res [Faraday::Response] the response object
69
+ # @return [Proc] the proc which performs the entity handling
70
+ def assign_entity(entity, res, &block)
71
+ lambda do
72
+ entity.assign_attributes(res.body.to_h)
73
+ entity.send(:changes_applied)
74
+ # We need to call +#changed?+ - the +@mutations_from_database+ is
75
+ # unset and this causes issues on subsequent calls to +#changed?+
76
+ # after a freeze (eg. when deleted)
77
+ entity.changed?
78
+ yield(entity) if block
79
+ entity
80
+ end
81
+ end
56
82
  end
57
83
  end
84
+ # rubocop:enable Metrics/BlockLength
58
85
  end
59
86
  end
60
87
  end
@@ -17,6 +17,7 @@ module PriceHubble
17
17
  include PriceHubble::EntityConcern::Attributes
18
18
  include PriceHubble::EntityConcern::Associations
19
19
  include PriceHubble::EntityConcern::Client
20
+ include PriceHubble::EntityConcern::Persistence
20
21
 
21
22
  # We collect all unknown attributes instead of raising while creating a new
22
23
  # instance. The unknown attributes are wrapped inside a
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PriceHubble
4
+ module EntityConcern
5
+ # Map some of the ActiveRecord::Persistence API methods for an entity
6
+ # instance for good compatibility. See: http://bit.ly/2W1rjfF and
7
+ # http://bit.ly/2ARRFYB
8
+ module Persistence
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ # A simple method to query for the state of the entity instance.
13
+ # Returns +false+ whenever the entity or the changes of it were not yet
14
+ # persisted on the remote application. This is helpful for creating new
15
+ # entities from scratch or checking for persisted updates.
16
+ #
17
+ # @return [Boolean] whenever persisted or not
18
+ def persisted?
19
+ return (new_record? ? false : !changed?) \
20
+ if respond_to? :id
21
+
22
+ false
23
+ end
24
+
25
+ # A simple method to query for the state of the entity instance.
26
+ # Returns +false+ whenever the entity is not yet created on the remote
27
+ # application. This is helpful for creating new entities from scratch.
28
+ #
29
+ # @return [Boolean] whenever persisted or not
30
+ def new_record?
31
+ return id.nil? if respond_to? :id
32
+
33
+ true
34
+ end
35
+
36
+ # Mark the entity instance as destroyed.
37
+ #
38
+ # @return [Hausgold::BaseEntity] the instance itself for method chaining
39
+ def mark_as_destroyed
40
+ @destroyed = true
41
+ self
42
+ end
43
+
44
+ # Returns true if this object has been destroyed, otherwise returns
45
+ # false.
46
+ #
47
+ # @return [Boolean] whenever the entity was destroyed or not
48
+ def destroyed?
49
+ @destroyed == true
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PriceHubble
4
+ # The PriceHubble dossier for a single property.
5
+ #
6
+ # @see https://docs.pricehubble.com/#international-dossier-creation
7
+ class Dossier < BaseEntity
8
+ # Configure the client to use
9
+ client :dossiers
10
+
11
+ # Mapped and tracked attributes
12
+ tracked_attr :id, :deal_type, :property, :country_code,
13
+ :title, :description, :asking_sale_price,
14
+ :valuation_override_sale_price, :valuation_override_rent_net,
15
+ :valuation_override_rent_gross,
16
+ :valuation_override_reason_freetext, :logo, :images
17
+
18
+ # Define attribute types for casting
19
+ typed_attr :deal_type, :enum, values: %i[sale rent]
20
+ typed_attr :country_code, :string_inquirer
21
+
22
+ # Associations
23
+ has_one :property, persist: true, initialize: true
24
+
25
+ # Set some defaults when initialized
26
+ after_initialize do
27
+ self.deal_type ||= :sale
28
+ self.country_code ||= 'DE'
29
+ end
30
+
31
+ # Save the dossier.
32
+ #
33
+ # @param args [Hash{Symbol => Mixed}] additional options
34
+ # @return [Boolean] the result state
35
+ def save(**args)
36
+ # When the current entity is already persisted, we send an update
37
+ if id.present?
38
+ # Skip making requests when the current entity is not dirty
39
+ return true unless changed?
40
+
41
+ # The current entity is dirty, so send an update request
42
+ return client.update_dossier(self, **args)
43
+ end
44
+
45
+ # Otherwise we send a new creation request
46
+ client.create_dossier(self, **args) && true
47
+ end
48
+
49
+ # Deletes the instance at the remote application and freezes this
50
+ # instance to reflect that no changes should be made (since they can't
51
+ # be persisted).
52
+ #
53
+ # @param args [Hash{Symbol => Mixed}] addition settings
54
+ # @return [PriceHubble::Dossier, false] whenever the deletion
55
+ # was successful
56
+ def delete(**args)
57
+ client.delete_dossier(self, **args) || false
58
+ end
59
+ alias destroy delete
60
+
61
+ # Create a new dossier share link.
62
+ #
63
+ # @param ttl [ActiveSupport::Duration] the time to live for the new link
64
+ # @param locale [String] the user frontend locale
65
+ # @param args [Hash{Symbol => Mixed}] additional options
66
+ # @return [String] the new dossier frontend link
67
+ def link(ttl: 365.days, locale: 'de_CH', lang: 'de', **args)
68
+ # Make sure the current dossier is saved before we try to generate a link
69
+ return unless save(**args)
70
+
71
+ # Send a "share dossier" request to the service
72
+ url = client.share_dossier(self, ttl: ttl, locale: locale, **args)
73
+ url += "?lang=#{lang}" if url
74
+ url
75
+ end
76
+
77
+ # Generate bang method variants
78
+ bangers :save, :link, :delete, :destroy
79
+ end
80
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PriceHubble
4
4
  # The version of the +price-hubble+ gem
5
- VERSION = '0.2.0'
5
+ VERSION = '0.4.2'
6
6
  end
data/lib/pricehubble.rb CHANGED
@@ -40,6 +40,7 @@ module PriceHubble
40
40
  autoload :Location, 'pricehubble/entity/location'
41
41
  autoload :Address, 'pricehubble/entity/address'
42
42
  autoload :Coordinates, 'pricehubble/entity/coordinates'
43
+ autoload :Dossier, 'pricehubble/entity/dossier'
43
44
 
44
45
  # Some general purpose utilities
45
46
  module Utils
@@ -77,6 +78,7 @@ module PriceHubble
77
78
  autoload :Base, 'pricehubble/client/base'
78
79
  autoload :Authentication, 'pricehubble/client/authentication'
79
80
  autoload :Valuation, 'pricehubble/client/valuation'
81
+ autoload :Dossiers, 'pricehubble/client/dossiers'
80
82
  end
81
83
 
82
84
  # Separated features of an entity instance
@@ -85,6 +87,7 @@ module PriceHubble
85
87
  autoload :Attributes, 'pricehubble/entity/concern/attributes'
86
88
  autoload :Associations, 'pricehubble/entity/concern/associations'
87
89
  autoload :Client, 'pricehubble/entity/concern/client'
90
+ autoload :Persistence, 'pricehubble/entity/concern/persistence'
88
91
 
89
92
  # Some custom typed attribute helpers
90
93
  module Attributes
data/pricehubble.gemspec CHANGED
@@ -14,6 +14,8 @@ Gem::Specification.new do |spec|
14
14
  spec.description = 'Ruby client for the PriceHubble REST API'
15
15
  spec.homepage = 'https://github.com/hausgold/pricehubble'
16
16
 
17
+ spec.required_ruby_version = '~> 2.5'
18
+
17
19
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
20
  f.match(%r{^(test|spec|features)/})
19
21
  end
@@ -21,23 +23,23 @@ Gem::Specification.new do |spec|
21
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
24
  spec.require_paths = ['lib']
23
25
 
24
- spec.add_runtime_dependency 'activemodel', '>= 4.2.0'
25
- spec.add_runtime_dependency 'activesupport', '>= 4.2.0'
26
- spec.add_runtime_dependency 'faraday', '~> 0.15'
27
- spec.add_runtime_dependency 'faraday_middleware', '~> 0.12'
26
+ spec.add_runtime_dependency 'activemodel', '>= 5.2.0'
27
+ spec.add_runtime_dependency 'activesupport', '>= 5.2.0'
28
+ spec.add_runtime_dependency 'faraday', '~> 1.0'
29
+ spec.add_runtime_dependency 'faraday_middleware', '~> 1.0'
28
30
  spec.add_runtime_dependency 'recursive-open-struct', '~> 1.1'
29
31
 
30
32
  spec.add_development_dependency 'appraisal'
31
33
  spec.add_development_dependency 'bundler', '>= 1.16', '< 3'
32
34
  spec.add_development_dependency 'factory_bot', '~> 4.11'
33
- spec.add_development_dependency 'railties', '>= 4.2.0'
35
+ spec.add_development_dependency 'railties', '>= 4.2.0', '< 6.1'
34
36
  spec.add_development_dependency 'rake', '~> 10.0'
35
37
  spec.add_development_dependency 'rdoc', '~> 6.1'
36
38
  spec.add_development_dependency 'redcarpet', '~> 3.4'
37
39
  spec.add_development_dependency 'rspec', '~> 3.0'
38
40
  spec.add_development_dependency 'rubocop', '~> 0.63.1'
39
41
  spec.add_development_dependency 'rubocop-rspec', '~> 1.31'
40
- spec.add_development_dependency 'simplecov', '~> 0.15'
42
+ spec.add_development_dependency 'simplecov', '< 0.18'
41
43
  spec.add_development_dependency 'terminal-table', '~> 1.8'
42
44
  spec.add_development_dependency 'timecop', '~> 0.9.1'
43
45
  spec.add_development_dependency 'vcr', '~> 3.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pricehubble
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hermann Mayer
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-10-22 00:00:00.000000000 Z
11
+ date: 2021-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -16,56 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.2.0
19
+ version: 5.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 4.2.0
26
+ version: 5.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 4.2.0
33
+ version: 5.2.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 4.2.0
40
+ version: 5.2.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: faraday
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.15'
47
+ version: '1.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.15'
54
+ version: '1.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: faraday_middleware
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.12'
61
+ version: '1.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.12'
68
+ version: '1.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: recursive-open-struct
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -135,6 +135,9 @@ dependencies:
135
135
  - - ">="
136
136
  - !ruby/object:Gem::Version
137
137
  version: 4.2.0
138
+ - - "<"
139
+ - !ruby/object:Gem::Version
140
+ version: '6.1'
138
141
  type: :development
139
142
  prerelease: false
140
143
  version_requirements: !ruby/object:Gem::Requirement
@@ -142,6 +145,9 @@ dependencies:
142
145
  - - ">="
143
146
  - !ruby/object:Gem::Version
144
147
  version: 4.2.0
148
+ - - "<"
149
+ - !ruby/object:Gem::Version
150
+ version: '6.1'
145
151
  - !ruby/object:Gem::Dependency
146
152
  name: rake
147
153
  requirement: !ruby/object:Gem::Requirement
@@ -230,16 +236,16 @@ dependencies:
230
236
  name: simplecov
231
237
  requirement: !ruby/object:Gem::Requirement
232
238
  requirements:
233
- - - "~>"
239
+ - - "<"
234
240
  - !ruby/object:Gem::Version
235
- version: '0.15'
241
+ version: '0.18'
236
242
  type: :development
237
243
  prerelease: false
238
244
  version_requirements: !ruby/object:Gem::Requirement
239
245
  requirements:
240
- - - "~>"
246
+ - - "<"
241
247
  - !ruby/object:Gem::Version
242
- version: '0.15'
248
+ version: '0.18'
243
249
  - !ruby/object:Gem::Dependency
244
250
  name: terminal-table
245
251
  requirement: !ruby/object:Gem::Requirement
@@ -332,11 +338,12 @@ extensions: []
332
338
  extra_rdoc_files: []
333
339
  files:
334
340
  - ".editorconfig"
341
+ - ".github/workflows/documentation.yml"
342
+ - ".github/workflows/test.yml"
335
343
  - ".gitignore"
336
344
  - ".rspec"
337
345
  - ".rubocop.yml"
338
346
  - ".simplecov"
339
- - ".travis.yml"
340
347
  - ".yardopts"
341
348
  - Appraisals
342
349
  - CHANGELOG.md
@@ -357,18 +364,18 @@ files:
357
364
  - doc/examples/authentication.rb
358
365
  - doc/examples/complex_property_valuations.rb
359
366
  - doc/examples/config.rb
367
+ - doc/examples/dossiers.rb
360
368
  - doc/examples/property_valuations_errors.rb
361
369
  - doc/examples/simple_property_valuations.rb
362
370
  - docker-compose.yml
363
- - gemfiles/rails_4.2.gemfile
364
- - gemfiles/rails_5.0.gemfile
365
- - gemfiles/rails_5.1.gemfile
366
371
  - gemfiles/rails_5.2.gemfile
372
+ - gemfiles/rails_6.0.gemfile
367
373
  - lib/price_hubble.rb
368
374
  - lib/pricehubble.rb
369
375
  - lib/pricehubble/client.rb
370
376
  - lib/pricehubble/client/authentication.rb
371
377
  - lib/pricehubble/client/base.rb
378
+ - lib/pricehubble/client/dossiers.rb
372
379
  - lib/pricehubble/client/request/data_sanitization.rb
373
380
  - lib/pricehubble/client/request/default_headers.rb
374
381
  - lib/pricehubble/client/response/data_sanitization.rb
@@ -390,7 +397,9 @@ files:
390
397
  - lib/pricehubble/entity/concern/attributes/string_inquirer.rb
391
398
  - lib/pricehubble/entity/concern/callbacks.rb
392
399
  - lib/pricehubble/entity/concern/client.rb
400
+ - lib/pricehubble/entity/concern/persistence.rb
393
401
  - lib/pricehubble/entity/coordinates.rb
402
+ - lib/pricehubble/entity/dossier.rb
394
403
  - lib/pricehubble/entity/location.rb
395
404
  - lib/pricehubble/entity/property.rb
396
405
  - lib/pricehubble/entity/property_conditions.rb
@@ -412,23 +421,23 @@ files:
412
421
  homepage: https://github.com/hausgold/pricehubble
413
422
  licenses: []
414
423
  metadata: {}
415
- post_install_message:
424
+ post_install_message:
416
425
  rdoc_options: []
417
426
  require_paths:
418
427
  - lib
419
428
  required_ruby_version: !ruby/object:Gem::Requirement
420
429
  requirements:
421
- - - ">="
430
+ - - "~>"
422
431
  - !ruby/object:Gem::Version
423
- version: '0'
432
+ version: '2.5'
424
433
  required_rubygems_version: !ruby/object:Gem::Requirement
425
434
  requirements:
426
435
  - - ">="
427
436
  - !ruby/object:Gem::Version
428
437
  version: '0'
429
438
  requirements: []
430
- rubygems_version: 3.0.6
431
- signing_key:
439
+ rubygems_version: 3.2.16
440
+ signing_key:
432
441
  specification_version: 4
433
442
  summary: Ruby client for the PriceHubble REST API
434
443
  test_files: []
data/.travis.yml DELETED
@@ -1,27 +0,0 @@
1
- env:
2
- global:
3
- - CC_TEST_REPORTER_ID=e80bc2fc948962da41f411047330599fe2bdafcc378bd99a1c3bffbfae74c20f
4
-
5
- sudo: false
6
- language: ruby
7
- cache: bundler
8
- rvm:
9
- - 2.6
10
- - 2.5
11
- - 2.4
12
- - 2.3
13
-
14
- gemfile:
15
- - gemfiles/rails_4.2.gemfile
16
- - gemfiles/rails_5.0.gemfile
17
- - gemfiles/rails_5.1.gemfile
18
- - gemfiles/rails_5.2.gemfile
19
-
20
- before_install: gem install bundler
21
- before_script:
22
- - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
23
- - chmod +x ./cc-test-reporter
24
- - ./cc-test-reporter before-build
25
- script: bundle exec rake
26
- after_script:
27
- - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This file was generated by Appraisal
4
-
5
- source 'https://rubygems.org'
6
-
7
- gem 'activemodel', '~> 4.2.11'
8
- gem 'activesupport', '~> 4.2.11'
9
- gem 'railties', '~> 4.2.11'
10
-
11
- gemspec path: '../'
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This file was generated by Appraisal
4
-
5
- source 'https://rubygems.org'
6
-
7
- gem 'activemodel', '~> 5.1.6'
8
- gem 'activesupport', '~> 5.1.6'
9
- gem 'railties', '~> 5.1.6'
10
-
11
- gemspec path: '../'