immoscout 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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