immoscout 1.0.0

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +30 -0
  3. data/.gitignore +14 -0
  4. data/.reek +6 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +15 -0
  7. data/.simplecov +3 -0
  8. data/.travis.yml +20 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/Gemfile +8 -0
  11. data/LICENSE +21 -0
  12. data/README.md +250 -0
  13. data/Rakefile +8 -0
  14. data/bin/console +14 -0
  15. data/bin/setup +8 -0
  16. data/doc/assets/logo.png +0 -0
  17. data/doc/assets/project.png +0 -0
  18. data/doc/assets/project.xcf +0 -0
  19. data/immoscout.gemspec +47 -0
  20. data/lib/immoscout.rb +27 -0
  21. data/lib/immoscout/api/client.rb +27 -0
  22. data/lib/immoscout/api/connection.rb +34 -0
  23. data/lib/immoscout/api/request.rb +33 -0
  24. data/lib/immoscout/configuration.rb +27 -0
  25. data/lib/immoscout/errors/failed.rb +11 -0
  26. data/lib/immoscout/models.rb +9 -0
  27. data/lib/immoscout/models/actions/attachment.rb +82 -0
  28. data/lib/immoscout/models/actions/contact.rb +75 -0
  29. data/lib/immoscout/models/actions/publish.rb +33 -0
  30. data/lib/immoscout/models/actions/real_estate.rb +122 -0
  31. data/lib/immoscout/models/apartment_buy.rb +90 -0
  32. data/lib/immoscout/models/base.rb +48 -0
  33. data/lib/immoscout/models/concerns/modelable.rb +55 -0
  34. data/lib/immoscout/models/concerns/propertiable.rb +56 -0
  35. data/lib/immoscout/models/concerns/renderable.rb +63 -0
  36. data/lib/immoscout/models/contact.rb +59 -0
  37. data/lib/immoscout/models/document.rb +31 -0
  38. data/lib/immoscout/models/house_buy.rb +87 -0
  39. data/lib/immoscout/models/parts/address.rb +26 -0
  40. data/lib/immoscout/models/parts/api_search_data.rb +19 -0
  41. data/lib/immoscout/models/parts/contact.rb +18 -0
  42. data/lib/immoscout/models/parts/coordinate.rb +18 -0
  43. data/lib/immoscout/models/parts/courtage.rb +19 -0
  44. data/lib/immoscout/models/parts/energy_certificate.rb +19 -0
  45. data/lib/immoscout/models/parts/energy_source.rb +17 -0
  46. data/lib/immoscout/models/parts/firing_type.rb +17 -0
  47. data/lib/immoscout/models/parts/geo_code.rb +18 -0
  48. data/lib/immoscout/models/parts/geo_hierarchy.rb +23 -0
  49. data/lib/immoscout/models/parts/price.rb +20 -0
  50. data/lib/immoscout/models/parts/publish_channel.rb +17 -0
  51. data/lib/immoscout/models/parts/real_estate.rb +17 -0
  52. data/lib/immoscout/models/parts/url.rb +18 -0
  53. data/lib/immoscout/models/parts/urls.rb +18 -0
  54. data/lib/immoscout/models/picture.rb +33 -0
  55. data/lib/immoscout/models/publish.rb +23 -0
  56. data/lib/immoscout/version.rb +5 -0
  57. metadata +268 -0
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "immoscout"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -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
Binary file
Binary file
Binary file
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "immoscout/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "immoscout"
9
+ spec.version = Immoscout::VERSION
10
+ spec.authors = ["Marcus Geissler"]
11
+ spec.email = ["marcus.geissler@hanseventures.com"]
12
+
13
+ spec.summary = 'Ruby client for the Immobilienscout24 REST API'
14
+ spec.homepage = "https://github.com/hausgold/immoscout"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org.
18
+ # To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this
20
+ # section to allow pushing to any host.
21
+ if spec.respond_to?(:metadata)
22
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
23
+ else
24
+ raise "RubyGems 2.0 or newer is required to protect against " \
25
+ "public gem pushes."
26
+ end
27
+
28
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
29
+ f.match(%r{^(test|spec|features)/})
30
+ end
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ["lib"]
34
+ spec.add_dependency "activesupport", ">= 3.2.0"
35
+ spec.add_dependency "faraday", "~> 0.13.0"
36
+ spec.add_dependency "faraday_middleware", "~> 0.12.0"
37
+ spec.add_dependency "simple_oauth", ">= 0.3"
38
+
39
+ spec.add_development_dependency "bundler", "~> 1.15"
40
+ spec.add_development_dependency "pry"
41
+ spec.add_development_dependency "rake", "~> 10.0"
42
+ spec.add_development_dependency "rspec", "~> 3.0"
43
+ spec.add_development_dependency "rspec-json_expectations", "~> 1.4.0"
44
+ spec.add_development_dependency "vcr", "~> 3.0.0"
45
+ spec.add_development_dependency "webmock", "~> 2.3.0"
46
+ spec.add_development_dependency "simplecov"
47
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require 'active_support/configurable'
5
+ require 'active_support/core_ext/hash'
6
+
7
+ require "immoscout/version"
8
+ require 'immoscout/configuration'
9
+ require 'immoscout/models'
10
+
11
+ module Immoscout
12
+ class << self
13
+ attr_writer :configuration
14
+ end
15
+
16
+ def self.configuration
17
+ @configuration ||= Configuration.new
18
+ end
19
+
20
+ def self.configure
21
+ yield(configuration)
22
+ end
23
+
24
+ def self.reset_configuration!
25
+ self.configuration = Configuration.new
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require 'singleton'
2
+ require_relative "connection"
3
+ require_relative "request"
4
+
5
+ module Immoscout
6
+ module Api
7
+ class Client
8
+ include Singleton
9
+ include Immoscout::Api::Connection
10
+ include Immoscout::Api::Request
11
+
12
+ attr_writer :user_name
13
+
14
+ def user_name
15
+ @user_name || config.user_name
16
+ end
17
+
18
+ def url
19
+ config.use_sandbox ? config.api_url_sandbox : config.api_url_live
20
+ end
21
+
22
+ def config
23
+ Immoscout.configuration
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,34 @@
1
+ require "faraday"
2
+ require "faraday_middleware"
3
+
4
+ module Immoscout
5
+ module Api
6
+ module Connection
7
+ # :reek:FeatureEnvy
8
+ def connection
9
+ @connection ||= Faraday::Connection.new(url: url) do |builder|
10
+ configure_oauth(builder)
11
+ builder.request :multipart
12
+ builder.request :url_encoded
13
+ builder.request :json
14
+ builder.response :follow_redirects
15
+ builder.response :json, content_type: /\bjson$/
16
+ builder.adapter :net_http
17
+ end
18
+ @connection
19
+ end
20
+
21
+ private
22
+
23
+ def configure_oauth(builder)
24
+ builder.request(
25
+ :oauth,
26
+ token: config.oauth_token,
27
+ token_secret: config.oauth_token_secret,
28
+ consumer_key: config.consumer_key,
29
+ consumer_secret: config.consumer_secret
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ module Immoscout
2
+ module Api
3
+ module Request
4
+ def get(path, payload = nil, multipart = nil)
5
+ request(:get, path, payload, multipart)
6
+ end
7
+
8
+ def post(path, payload = nil, multipart = nil)
9
+ request(:post, path, payload, multipart)
10
+ end
11
+
12
+ def put(path, payload = nil, multipart = nil)
13
+ request(:put, path, payload, multipart)
14
+ end
15
+
16
+ def delete(path, payload = nil, multipart = nil)
17
+ request(:delete, path, payload, multipart)
18
+ end
19
+
20
+ def request(method, path, payload = nil, multipart = nil)
21
+ connection.send(method, path, multipart) do |request|
22
+ if multipart
23
+ request.headers['Content-Type'] = "multipart/form-data"
24
+ else
25
+ request.body = payload if payload
26
+ request.headers['Content-Type'] = "application/json;charset=UTF-8"
27
+ end
28
+ request.headers['Accept'] = "application/json"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Immoscout
4
+ class Configuration
5
+ include ActiveSupport::Configurable
6
+ config_accessor(:consumer_key) { ENV["IMMOSCOUT_CONSUMER_KEY"] }
7
+ config_accessor(:consumer_secret) { ENV["IMMOSCOUT_CONSUMER_SECRET"] }
8
+
9
+ config_accessor(:oauth_token) { ENV["IMMOSCOUT_OAUTH_TOKEN"] }
10
+ config_accessor(:oauth_token_secret) { ENV["IMMOSCOUT_OAUTH_TOKEN_SECRET"] }
11
+
12
+ config_accessor(:use_sandbox) { false }
13
+
14
+ config_accessor(:api_version) { "v1.0" }
15
+
16
+ config_accessor(:user_name) { 'me' }
17
+
18
+ config_accessor(:api_url_live) do
19
+ "https://rest.immobilienscout24.de/" \
20
+ "restapi/api/offer/#{api_version}"
21
+ end
22
+ config_accessor(:api_url_sandbox) do
23
+ "https://rest.sandbox-immobilienscout24.de/" \
24
+ "restapi/api/offer/#{api_version}"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module Immoscout
2
+ module Errors
3
+ class Failed < StandardError
4
+ def initialize(response)
5
+ @status = response.status
6
+ @body = response.body
7
+ super(["HTTP #{@status}", @body])
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'errors/failed'
2
+ require_relative 'api/client'
3
+ # renderable models
4
+ require_relative 'models/house_buy'
5
+ require_relative 'models/apartment_buy'
6
+ require_relative 'models/contact'
7
+ require_relative 'models/publish'
8
+ require_relative 'models/picture'
9
+ require_relative 'models/document'
@@ -0,0 +1,82 @@
1
+ require 'json'
2
+ require_relative '../concerns/modelable'
3
+
4
+ module Immoscout
5
+ module Models
6
+ module Actions
7
+ module Attachment
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ include Immoscout::Models::Concerns::Modelable
12
+
13
+ self.unpack_collection = proc do |hash|
14
+ hash
15
+ .fetch("common.attachments")
16
+ .first
17
+ .fetch("attachment")
18
+ end
19
+
20
+ def save
21
+ attachable_id = attachable.try(:id) || attachable
22
+ response = api.post(
23
+ "user/#{api.user_name}/realestate/#{attachable_id}/attachment",
24
+ nil,
25
+ attachment: Faraday::UploadIO.new(file, content_type, file_name),
26
+ metadata: as_json
27
+ )
28
+ handle_response(response)
29
+ self.id = id_from_response(response)
30
+ self
31
+ end
32
+
33
+ def destroy
34
+ attachable_id = attachable.try(:id) || attachable
35
+ response = api.delete(
36
+ "user/#{api.user_name}/realestate/#{attachable_id}/attachment/#{id}",
37
+ )
38
+ handle_response(response)
39
+ self
40
+ end
41
+
42
+ private
43
+
44
+ def file_extension
45
+ File.extname(file_name)
46
+ end
47
+
48
+ def file_name
49
+ File.basename(file)
50
+ end
51
+
52
+ def content_type
53
+ self.class.content_type_from_extension file_extension.downcase
54
+ end
55
+ end
56
+
57
+ class_methods do
58
+ def content_type_from_extension(ext)
59
+ {
60
+ ".jpg" => "image/jpeg",
61
+ ".jpeg" => "image/jpeg",
62
+ ".gif" => "image/gif",
63
+ ".png" => "image/png",
64
+ ".pdf" => "application/pdf"
65
+ }.fetch(ext)
66
+ end
67
+
68
+ def all(real_estate_id)
69
+ response = api.get(
70
+ "user/#{api.user_name}/realestate/#{real_estate_id}/attachment"
71
+ )
72
+ handle_response(response)
73
+ objects = unpack_collection.call(response.body)
74
+ objects
75
+ .map { |object| new(object) }
76
+ .select { |object| object.type =~ /#{name.demodulize}/i }
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,75 @@
1
+ require 'json'
2
+ require_relative '../concerns/modelable'
3
+
4
+ module Immoscout
5
+ module Models
6
+ module Actions
7
+ module Contact
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ include Immoscout::Models::Concerns::Modelable
12
+
13
+ self.unpack_collection = proc do |hash|
14
+ hash
15
+ .fetch("common.realtorContactDetailsList", {})
16
+ .fetch("realtorContactDetails", nil)
17
+ end
18
+
19
+ def save
20
+ response = \
21
+ if id
22
+ api.put("user/#{api.user_name}/contact/#{id}", as_json)
23
+ else
24
+ api.post("user/#{api.user_name}/contact", as_json)
25
+ end
26
+
27
+ handle_response(response)
28
+ self.id = id_from_response(response) unless id
29
+ self
30
+ end
31
+
32
+ def destroy
33
+ response = api.delete("user/#{api.user_name}/contact/#{id}")
34
+ handle_response(response)
35
+ self
36
+ end
37
+ end
38
+
39
+ class_methods do
40
+ def find(id)
41
+ response = api.get("user/#{api.user_name}/contact/#{id}")
42
+ handle_response(response)
43
+ from_raw(response.body)
44
+ end
45
+
46
+ def find_by(hash)
47
+ external_id = hash.symbolize_keys.fetch(:external_id)
48
+ find("ext-#{external_id}")
49
+ end
50
+
51
+ def all
52
+ response = api.get("user/#{api.user_name}/contact")
53
+ handle_response(response)
54
+ objects = unpack_collection.call(response.body)
55
+ objects.map { |object| new(object) }
56
+ end
57
+
58
+ def first
59
+ all.first
60
+ end
61
+
62
+ def last
63
+ all.last
64
+ end
65
+
66
+ def create(hash)
67
+ instance = new(hash)
68
+ instance.save
69
+ instance
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,33 @@
1
+ require 'json'
2
+ require_relative '../concerns/modelable'
3
+
4
+ module Immoscout
5
+ module Models
6
+ module Actions
7
+ module Publish
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ include Immoscout::Models::Concerns::Modelable
12
+
13
+ def save
14
+ response = api.post("publish", as_json)
15
+ handle_response(response)
16
+ self
17
+ end
18
+
19
+ def destroy
20
+ response = api.delete(
21
+ "publish/#{real_estate.id}_#{publish_channel.id}"
22
+ )
23
+ handle_response(response)
24
+ self
25
+ end
26
+ end
27
+
28
+ class_methods do
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end