vrbo 1.1.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e7a1740a7b58843bfc500c961fc306a65062547
4
- data.tar.gz: fb2f0f9d024bd079b6e70105c84d304eff56737a
3
+ metadata.gz: 292464634aa7ba985ef18252adaf0cfc66554524
4
+ data.tar.gz: b3bd501182bb1ea02ac9d35bee0778013c2a3a74
5
5
  SHA512:
6
- metadata.gz: ef04241e73ebb3c63bd1ce2010317a5842d83c33d69a1ff619ac244a5b2bf7a9d79d933bc2e5ba5b6ec27876c1a99641e5426a1de0a2f6f3187886d12d749d97
7
- data.tar.gz: b57dba1d7a5c60a32a62f591ce33a6c3536849dbc49cc556cec460031649075cf65fc0a2ed9b49de653f914c92e11f3bce3dc7ea6e7564ee4d359f79b1bd6c30
6
+ metadata.gz: 4815b659820d192c4f4ef80d05bb3c87b5afdc5ace663ec6fd4449dace169b4862462d6ec7a22aa9179e54488e5644d99274eb582e1ade9b840340b7fee01df6
7
+ data.tar.gz: df2a0fee297894982bcb946fb6cbc12579eb12014cb1e0fb21a709f486efdf2ffa0fb62085fe737c9407cc14d1653593874a723180c5ed332d389c0b2428d173
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/README.md CHANGED
@@ -5,37 +5,28 @@ scrape this calendar: [http://www.vrbo.com/293021/calendar](http://www.vrbo.com/
5
5
 
6
6
  ## Installation
7
7
 
8
- Add this line to your application's Gemfile:
9
-
10
- gem 'vrbo'
11
-
12
- And then execute:
13
-
14
- $ bundle
15
-
16
- Or install it yourself as:
17
-
18
8
  $ gem install vrbo
19
9
 
20
10
  ## Usage
21
11
 
22
12
  If you're just working with one calendar, then you can specify the VRBO calendar id in an initializer:
23
13
 
24
- # config/initializers/vrbo_calendar.rb
25
-
26
- VRBO.configure do |config|
27
- config.calendar_id = 293021
28
- end
14
+ ```ruby
15
+ # config/initializers/vrbo.rb
16
+ VRBO.configure do |config|
17
+ config.calendar_id = 293021
18
+ end
19
+ ```
29
20
 
30
21
  Then to lookup available dates and see if a date range is available:
31
22
 
32
- calendar = VRBO::Calendar.new
33
- calendar.find_available_dates
34
- calendar.available?(Date.today, Date.tomorrow)
35
- #=> true/false
36
-
37
- These two methods are also available as class methods, however `available?` will then need an array of dates
38
- as the third parameter.
23
+ ```ruby
24
+ calendar = VRBO::Calendar.new
25
+ calendar.available?(Date.today, Date.today + 5)
26
+ #=> true/false
27
+ calendar.available_dates
28
+ #=> ["2015-11-03", "2015-11-04", "2015-11-05", ...]
29
+ ```
39
30
 
40
31
  ## Contributing
41
32
 
data/Rakefile CHANGED
@@ -1,9 +1,8 @@
1
- require 'bundler/gem_tasks'
2
- require 'rake'
3
- require 'rake/testtask'
1
+ require 'rspec/core/rake_task'
4
2
 
5
- task default: :test
6
-
7
- Rake::TestTask.new do |t|
8
- t.pattern = 'test/**/*_test.rb'
3
+ RSpec::Core::RakeTask.new(:spec) do |spec|
4
+ spec.pattern = 'spec/**/*_spec.rb'
9
5
  end
6
+
7
+ desc 'Run specs'
8
+ task default: :spec
data/lib/vrbo.rb CHANGED
@@ -2,14 +2,10 @@ require 'date'
2
2
  require 'uri'
3
3
  require 'net/https'
4
4
  require 'mechanize'
5
-
6
5
  require 'vrbo/version'
7
- require 'vrbo/configuration'
8
- require 'vrbo/availability'
9
- require 'vrbo/calendar'
10
6
 
11
7
  module VRBO
12
- extend self
8
+ module_function
13
9
 
14
10
  def configure
15
11
  yield config
@@ -23,4 +19,7 @@ module VRBO
23
19
  @config = nil
24
20
  end
25
21
 
26
- end
22
+ autoload :Configuration, 'vrbo/configuration'
23
+ autoload :Availability, 'vrbo/availability'
24
+ autoload :Calendar, 'vrbo/calendar'
25
+ end
@@ -28,4 +28,4 @@ module VRBO
28
28
  count
29
29
  end
30
30
  end
31
- end
31
+ end
data/lib/vrbo/calendar.rb CHANGED
@@ -1,19 +1,21 @@
1
- require 'vrbo/class_methods'
2
-
3
1
  module VRBO
4
2
  class Calendar
5
- extend ClassMethods
6
3
 
7
- attr_accessor :id, :available_dates, :days
4
+ attr_accessor :id, :days
8
5
 
9
6
  def initialize(calendar_id = nil)
10
7
  @id = calendar_id || VRBO.config.calendar_id
11
8
  @days = {}
12
- @available_dates = []
9
+ @available_dates = nil
10
+ end
11
+
12
+ def available_dates
13
+ @available_dates ||= Date.today.upto(Date.today + 365).map { |date| date_if_available(date) }.compact
13
14
  end
14
15
 
15
- def available?(arrival, depart, my_dates = nil)
16
- dates = my_dates || available_dates
16
+ # @description exclusive, drops day from departure because bookings usually go on per nightly
17
+ def available?(arrival, depart, dates = nil)
18
+ dates = dates || available_dates
17
19
  available = dates.any?
18
20
  arrival.upto(depart - 1).each do |date|
19
21
  available = false unless dates.include?(date.to_s)
@@ -21,22 +23,17 @@ module VRBO
21
23
  available
22
24
  end
23
25
 
24
- def find_available_dates
25
- today = Date.today
26
- @available_dates = today.upto(today + 365).map { |date| date_if_available(date) }.compact
27
- end
28
-
29
26
  def url(protocol = 'http')
30
27
  if id
31
28
  "#{protocol}://www.vrbo.com/#{id}/calendar"
32
29
  else
33
- raise ArgumentError, 'You must provide a calendar id'
30
+ fail ArgumentError, 'calendar_id is required! You can initialize with a calendar_id or configure the module VRBO'
34
31
  end
35
32
  end
36
33
 
37
- alias :find_all_available_dates :find_available_dates
38
-
39
- private
34
+ #
35
+ # Private
36
+ #
40
37
 
41
38
  def date_if_available(date)
42
39
  m = date.month.to_s
@@ -45,24 +42,28 @@ module VRBO
45
42
  end
46
43
 
47
44
  def collect_days_for_month(date)
48
- scrape_table_for(date).map { |cell| cell.children.to_s.strip }
45
+ table = calendar.search('.cal-month').at("//b[contains(text(), '#{date.strftime('%B %Y')}')]/following-sibling::table")
46
+ table.search('td:not(.strike)').map { |cell| cell.children.to_s.strip }
47
+ rescue => e
48
+ puts e.class
49
+ puts e.message
50
+ puts e.backtrace
51
+ puts calendar: calendar
49
52
  end
50
53
 
51
- def scrape_table_for(date)
52
- calendar.search('.cal-month').at(table_xpath(date)).search('td:not(.strike)')
53
- end
54
+ # , retries: 10
55
+ # collect_days_for_month(date, retries: retries - 1) if table.nil? && retries > 0
54
56
 
55
57
  def calendar
56
- @calendar ||= agent.get(url)
57
- end
58
-
59
- def agent
60
- @agent ||= Mechanize.new
61
- end
62
-
63
- # e.g. March 2014
64
- def table_xpath(date)
65
- "//b[contains(text(), '#{date.strftime('%B %Y')}')]/following-sibling::table"
58
+ @calendar ||= Mechanize.start do |agent|
59
+ agent.open_timeout = 10
60
+ agent.read_timeout = 10
61
+ agent.follow_meta_refresh = true
62
+ agent.keep_alive = true
63
+ agent.max_history = 1
64
+ agent.user_agent_alias = 'Mac Safari'
65
+ agent.get(url).parser
66
+ end
66
67
  end
67
68
  end
68
- end
69
+ end
@@ -2,4 +2,4 @@ module VRBO
2
2
  class Configuration
3
3
  attr_accessor :calendar_id
4
4
  end
5
- end
5
+ end
data/lib/vrbo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module VRBO
2
- VERSION = '1.1.0'
2
+ VERSION = '2.0.1'
3
3
  end
@@ -0,0 +1,247 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://www.vrbo.com/293021/calendar
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip,deflate,identity
12
+ Accept:
13
+ - '*/*'
14
+ User-Agent:
15
+ - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.51.22 (KHTML,
16
+ like Gecko) Version/5.1.1 Safari/534.51.22
17
+ Accept-Charset:
18
+ - ISO-8859-1,utf-8;q=0.7,*;q=0.7
19
+ Accept-Language:
20
+ - en-us,en;q=0.5
21
+ Host:
22
+ - www.vrbo.com
23
+ Connection:
24
+ - keep-alive
25
+ Keep-Alive:
26
+ - 300
27
+ response:
28
+ status:
29
+ code: 200
30
+ message: OK
31
+ headers:
32
+ Server:
33
+ - nginx
34
+ Date:
35
+ - Wed, 04 Nov 2015 02:44:58 GMT
36
+ Content-Type:
37
+ - text/html; charset=utf-8
38
+ Transfer-Encoding:
39
+ - chunked
40
+ Connection:
41
+ - keep-alive
42
+ Vary:
43
+ - Accept-Encoding
44
+ Cache-Control:
45
+ - private
46
+ Set-Cookie:
47
+ - PpcCampaign=; path=/
48
+ - __RequestVerificationToken=GUOOk32Gm_UNUeaktYp8m1TJSb_b-wgIv-Q_7yYiK_ZzqQmjAt95jnYg6hOTlPsTJ92AUeoQbt7M5tWC_s9cH6IeO7g1;
49
+ path=/; HttpOnly
50
+ - hal=ga=0&si=1&ui=0&vi=1&pr=1; expires=Thu, 05-Nov-2015 02:44:58 GMT; path=/
51
+ - has=311c654d-3316-44e4-8a17-854a00afc4a8; expires=Thu, 05-Nov-2015 02:44:58
52
+ GMT; path=/
53
+ - hav=196004f3-753a-410d-96df-7dde37be9bf0; expires=Sat, 04-Nov-2017 01:44:58
54
+ GMT; path=/
55
+ - pr_buckets=buckets=vbr%3a1%2cvbq%3a0%2cvbp%3a1%2cvbl%3a1%2cvbz%3a0%2cvbw%3a1%2cvbaa%3a0%2cvbv%3a1%2cvbu%3a0%2cvrbobceheader%3a0%2cvba%3a1%2cvbc%3a1%2cvbg%3a0%2cvbi%3a0%2cvbk%3a0;
56
+ expires=Wed, 04-Nov-2015 02:49:58 GMT; path=/
57
+ - pr_groups=forceGroups=; expires=Fri, 04-Dec-2015 02:44:58 GMT; path=/
58
+ X-Powered-By:
59
+ - ASP.NET
60
+ Content-Encoding:
61
+ - gzip
62
+ body:
63
+ encoding: ASCII-8BIT
64
+ string: !binary |-
65
+ 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
245
+ http_version:
246
+ recorded_at: Wed, 04 Nov 2015 02:44:59 GMT
247
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,105 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ require 'vcr'
18
+ require 'rspec/its'
19
+
20
+ VCR.configure do |config|
21
+ config.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
22
+ config.hook_into :webmock
23
+ end
24
+
25
+ require 'vrbo'
26
+
27
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
28
+ RSpec.configure do |config|
29
+ # rspec-expectations config goes here. You can use an alternate
30
+ # assertion/expectation library such as wrong or the stdlib/minitest
31
+ # assertions if you prefer.
32
+ config.expect_with :rspec do |expectations|
33
+ # This option will default to `true` in RSpec 4. It makes the `description`
34
+ # and `failure_message` of custom matchers include text for helper methods
35
+ # defined using `chain`, e.g.:
36
+ # be_bigger_than(2).and_smaller_than(4).description
37
+ # # => "be bigger than 2 and smaller than 4"
38
+ # ...rather than:
39
+ # # => "be bigger than 2"
40
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
41
+ end
42
+
43
+ # rspec-mocks config goes here. You can use an alternate test double
44
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
45
+ config.mock_with :rspec do |mocks|
46
+ # Prevents you from mocking or stubbing a method that does not exist on
47
+ # a real object. This is generally recommended, and will default to
48
+ # `true` in RSpec 4.
49
+ mocks.verify_partial_doubles = true
50
+ end
51
+
52
+ # The settings below are suggested to provide a good initial experience
53
+ # with RSpec, but feel free to customize to your heart's content.
54
+ =begin
55
+ # These two settings work together to allow you to limit a spec run
56
+ # to individual examples or groups you care about by tagging them with
57
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
58
+ # get run.
59
+ config.filter_run :focus
60
+ config.run_all_when_everything_filtered = true
61
+
62
+ # Allows RSpec to persist some state between runs in order to support
63
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
64
+ # you configure your source control system to ignore this file.
65
+ config.example_status_persistence_file_path = "spec/examples.txt"
66
+
67
+ # Limits the available syntax to the non-monkey patched syntax that is
68
+ # recommended. For more details, see:
69
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
70
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
71
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
72
+ config.disable_monkey_patching!
73
+
74
+ # This setting enables warnings. It's recommended, but in some cases may
75
+ # be too noisy due to issues in dependencies.
76
+ config.warnings = true
77
+
78
+ # Many RSpec users commonly either run the entire suite or an individual
79
+ # file, and it's useful to allow more verbose output when running an
80
+ # individual spec file.
81
+ if config.files_to_run.one?
82
+ # Use the documentation formatter for detailed output,
83
+ # unless a formatter has already been configured
84
+ # (e.g. via a command-line flag).
85
+ config.default_formatter = 'doc'
86
+ end
87
+
88
+ # Print the 10 slowest examples and example groups at the
89
+ # end of the spec run, to help surface which specs are running
90
+ # particularly slow.
91
+ config.profile_examples = 10
92
+
93
+ # Run specs in random order to surface order dependencies. If you find an
94
+ # order dependency and want to debug it, you can fix the order by providing
95
+ # the seed, which is printed after each run.
96
+ # --seed 1234
97
+ config.order = :random
98
+
99
+ # Seed global randomization in this process using the `--seed` CLI option.
100
+ # Setting this allows you to use `--seed` to deterministically reproduce
101
+ # test failures related to randomization by passing the same `--seed` value
102
+ # as the one that triggered the failure.
103
+ Kernel.srand config.seed
104
+ =end
105
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe VRBO::Availability do
4
+ let(:dates) { %w[2015-12-13 2015-12-14 2015-12-15 2015-12-16 2015-12-17 2015-12-20] }
5
+
6
+ subject { described_class.new(dates) }
7
+
8
+ describe '#initialize' do
9
+ context 'when given an array of dates strings' do
10
+ it 'assigns @start_at to the date parsed first item' do
11
+ expect(subject.start_at).to eq Date.parse('2015-12-13')
12
+ end
13
+
14
+ it 'removes the first item from the array (mutation)' do
15
+ expect {
16
+ subject.start_at
17
+ }.to change(dates, :length).from(6).to(5)
18
+ end
19
+
20
+ it 'assigns the all but the first items in -dates- to @dates' do
21
+ expect(subject.dates).to eq %w[2015-12-14 2015-12-15 2015-12-16 2015-12-17 2015-12-20]
22
+ end
23
+ end
24
+
25
+ context 'when given nil' do
26
+ let(:dates) { nil }
27
+
28
+ it 'assigns @start_at to today' do
29
+ expect(subject.start_at).to eq Date.today
30
+ end
31
+ end
32
+ end
33
+
34
+ describe '#duration' do
35
+ context 'when given a list of dates in ascending order' do
36
+ it 'returns the length of the first sequence' do
37
+ expect(subject.duration).to eq 5
38
+ end
39
+
40
+ context 'when dates are sequential' do
41
+ let(:dates) { %w[2015-12-05 2015-12-07 2015-12-10] }
42
+
43
+ it 'returns one' do
44
+ expect(subject.duration).to eq 1
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'when no dates are given' do
50
+ let(:dates) { [] }
51
+
52
+ it 'returns one' do
53
+ expect(subject.duration).to eq 1
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,151 @@
1
+ require 'spec_helper'
2
+
3
+ describe VRBO::Calendar do
4
+ let(:calendar_id) { '293021' }
5
+
6
+ subject { described_class.new calendar_id }
7
+
8
+ describe '#initialize' do
9
+ its(:id) { should == calendar_id }
10
+ its(:days) { should == {} }
11
+
12
+ context 'when no -calendar_id given' do
13
+ let(:calendar_id) { nil }
14
+
15
+ before do
16
+ VRBO.configure do |config|
17
+ config.calendar_id = calendar_id
18
+ end
19
+ end
20
+
21
+ it 'uses the configuration value' do
22
+ expect(subject.id).to eq calendar_id
23
+ end
24
+
25
+ after do
26
+ VRBO.reset_config
27
+ end
28
+ end
29
+ end
30
+
31
+ describe '#available_dates' do
32
+ it 'returns the available dates for the next year' do
33
+ VCR.use_cassette(calendar_id) do
34
+ expect(subject.available_dates.length).to eq 284
35
+ expect(subject.available_dates).to eq %w[2015-11-08 2015-11-09 2015-11-22 2015-11-23 2015-11-24 2015-11-25 2015-11-26 2015-11-29 2015-11-30 2015-12-10 2015-12-13 2015-12-14 2015-12-15 2015-12-16 2015-12-17 2015-12-18 2015-12-22 2015-12-23 2015-12-24 2015-12-31 2016-01-01 2016-01-05 2016-01-06 2016-01-07 2016-01-08 2016-01-11 2016-01-12 2016-01-19 2016-01-20 2016-01-21 2016-01-22 2016-01-23 2016-01-24 2016-01-25 2016-01-26 2016-01-27 2016-01-28 2016-01-29 2016-01-30 2016-01-31 2016-02-01 2016-02-06 2016-02-07 2016-02-08 2016-02-09 2016-02-10 2016-02-11 2016-02-20 2016-02-21 2016-02-25 2016-03-02 2016-03-03 2016-03-04 2016-03-05 2016-03-06 2016-03-07 2016-03-08 2016-03-09 2016-03-10 2016-03-11 2016-03-16 2016-03-17 2016-03-18 2016-03-19 2016-03-20 2016-03-21 2016-03-26 2016-03-27 2016-03-28 2016-03-29 2016-03-30 2016-03-31 2016-04-01 2016-04-02 2016-04-03 2016-04-04 2016-04-05 2016-04-06 2016-04-07 2016-04-08 2016-04-09 2016-04-10 2016-04-11 2016-04-12 2016-04-13 2016-04-14 2016-04-15 2016-04-16 2016-04-17 2016-04-18 2016-04-19 2016-04-20 2016-04-21 2016-04-22 2016-04-23 2016-04-24 2016-04-25 2016-04-26 2016-04-27 2016-04-28 2016-04-29 2016-04-30 2016-05-01 2016-05-02 2016-05-03 2016-05-04 2016-05-05 2016-05-06 2016-05-07 2016-05-08 2016-05-09 2016-05-10 2016-05-11 2016-05-12 2016-05-13 2016-05-14 2016-05-15 2016-05-16 2016-05-17 2016-05-18 2016-05-19 2016-05-20 2016-05-21 2016-05-22 2016-05-23 2016-05-24 2016-05-25 2016-05-26 2016-05-27 2016-05-28 2016-05-29 2016-05-30 2016-05-31 2016-06-01 2016-06-02 2016-06-03 2016-06-04 2016-06-05 2016-06-06 2016-06-07 2016-06-08 2016-06-09 2016-06-10 2016-06-11 2016-06-12 2016-06-13 2016-06-14 2016-06-15 2016-06-16 2016-06-17 2016-06-18 2016-06-19 2016-06-20 2016-06-21 2016-06-22 2016-06-23 2016-06-24 2016-06-27 2016-06-28 2016-06-29 2016-06-30 2016-07-01 2016-07-02 2016-07-03 2016-07-04 2016-07-05 2016-07-06 2016-07-07 2016-07-08 2016-07-09 2016-07-10 2016-07-11 2016-07-12 2016-07-13 2016-07-14 2016-07-15 2016-07-16 2016-07-17 2016-07-18 2016-07-19 2016-07-20 2016-07-21 2016-07-22 2016-07-23 2016-07-24 2016-07-25 2016-07-26 2016-07-27 2016-07-28 2016-07-29 2016-07-30 2016-07-31 2016-08-01 2016-08-02 2016-08-03 2016-08-04 2016-08-05 2016-08-06 2016-08-07 2016-08-08 2016-08-09 2016-08-10 2016-08-11 2016-08-12 2016-08-13 2016-08-14 2016-08-15 2016-08-16 2016-08-17 2016-08-18 2016-08-19 2016-08-20 2016-08-21 2016-08-22 2016-08-23 2016-08-24 2016-08-25 2016-08-26 2016-08-27 2016-08-28 2016-08-29 2016-08-30 2016-08-31 2016-09-01 2016-09-02 2016-09-03 2016-09-04 2016-09-05 2016-09-06 2016-09-07 2016-09-08 2016-09-09 2016-09-10 2016-09-11 2016-09-12 2016-09-13 2016-09-14 2016-09-15 2016-09-16 2016-09-17 2016-09-18 2016-09-19 2016-09-20 2016-09-21 2016-09-22 2016-09-23 2016-09-24 2016-09-25 2016-09-26 2016-09-27 2016-09-28 2016-09-29 2016-09-30 2016-10-01 2016-10-02 2016-10-03 2016-10-04 2016-10-05 2016-10-06 2016-10-07 2016-10-08 2016-10-09 2016-10-10 2016-10-11 2016-10-12 2016-10-13 2016-10-14 2016-10-15 2016-10-16 2016-10-17 2016-10-18 2016-10-19 2016-10-20 2016-10-21 2016-10-22 2016-10-23 2016-10-24 2016-10-25 2016-10-26 2016-10-27 2016-10-28 2016-10-29 2016-10-30 2016-10-31]
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#available?' do
41
+ let(:arrival) { Date.new(2015, 11, 8) }
42
+ let(:departure) { Date.new(2015, 11, 9) }
43
+
44
+ context 'when the requested dates are available' do
45
+ context 'when range is two days (1 night)' do
46
+ it 'returns true' do
47
+ VCR.use_cassette(calendar_id) do
48
+ expect(subject.available?(arrival, departure)).to eq true
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'when range is three days (2 nights)' do
54
+ let(:departure) { Date.new(2015, 11, 10) }
55
+
56
+ it 'returns true' do
57
+ VCR.use_cassette(calendar_id) do
58
+ expect(subject.available?(arrival, departure)).to eq true
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'when the requested dates are unavailable' do
65
+ let(:arrival) { Date.new(2015, 11, 22) }
66
+ let(:departure) { Date.new(2015, 11, 28) }
67
+
68
+ it 'returns false' do
69
+ VCR.use_cassette(calendar_id) do
70
+ expect(subject.available?(arrival, departure)).to eq false
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'when -dates- is empty' do
76
+ it 'returns false' do
77
+ VCR.use_cassette(calendar_id) do
78
+ expect(subject).to receive(:available_dates).and_return []
79
+ expect(subject.available?(arrival, departure)).to eq false
80
+ end
81
+ end
82
+ end
83
+
84
+ context 'when the third argument -dates- is given' do
85
+ let(:dates) { %w[2015-12-13 2015-12-14 2015-12-15 2015-12-16 2015-12-17 2015-12-18] }
86
+
87
+ it 'does not call +available_dates+' do
88
+ VCR.use_cassette(calendar_id) do
89
+ expect(subject).to_not receive(:available_dates)
90
+ subject.available?(arrival, departure, dates)
91
+ end
92
+ end
93
+
94
+ context 'when the requested dates are available' do
95
+ let(:arrival) { Date.new(2015, 12, 14) }
96
+ let(:departure) { Date.new(2015, 12, 19) }
97
+
98
+ it 'returns true' do
99
+ VCR.use_cassette(calendar_id) do
100
+ expect(subject.available?(arrival, departure, dates)).to be true
101
+ end
102
+ end
103
+ end
104
+
105
+ context 'when the requested dates are unavailable' do
106
+ it 'returns false' do
107
+ VCR.use_cassette(calendar_id) do
108
+ expect(subject.available?(arrival, departure, dates)).to be false
109
+ end
110
+ end
111
+ end
112
+
113
+ context 'when -dates- are empty' do
114
+ let(:dates) { [] }
115
+
116
+ it 'returns false' do
117
+ VCR.use_cassette(calendar_id) do
118
+ expect(subject).to_not receive(:available_dates)
119
+ expect(subject.available?(arrival, departure, dates)).to be false
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ describe '#url' do
127
+ context 'when @id is present' do
128
+ context 'when -protocol- is given' do
129
+ it 'returns the url using the given protocol' do
130
+ expect(subject.url('https')).to eq "https://www.vrbo.com/#{calendar_id}/calendar"
131
+ end
132
+ end
133
+
134
+ context 'when -protocol- is given' do
135
+ it 'returns the url using the default "http" protocol' do
136
+ expect(subject.url).to eq "http://www.vrbo.com/#{calendar_id}/calendar"
137
+ end
138
+ end
139
+ end
140
+
141
+ context 'when @id is missing' do
142
+ let(:calendar_id) { nil }
143
+
144
+ it 'raises an argument error' do
145
+ expect {
146
+ subject.url
147
+ }.to raise_error(ArgumentError, /calendar_id is required/)
148
+ end
149
+ end
150
+ end
151
+ end
data/vrbo.gemspec CHANGED
@@ -23,4 +23,8 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.add_development_dependency "bundler", "~> 1.3"
25
25
  spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "vcr", '~> 2.9'
27
+ spec.add_development_dependency "webmock", '~> 1.21'
28
+ spec.add_development_dependency "rspec", '~> 3.3'
29
+ spec.add_development_dependency "rspec-its", '~> 1.2'
26
30
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vrbo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Buckley
@@ -66,6 +66,62 @@ dependencies:
66
66
  - - '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: vcr
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '2.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '2.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '1.21'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.21'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '3.3'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '3.3'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec-its
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '1.2'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: '1.2'
69
125
  description: Scrapes a VRBO calendar using Mechanize and returns available dates
70
126
  email:
71
127
  - arebuckley@gmail.com
@@ -74,6 +130,7 @@ extensions: []
74
130
  extra_rdoc_files: []
75
131
  files:
76
132
  - .gitignore
133
+ - .rspec
77
134
  - .ruby-gemset
78
135
  - .ruby-version
79
136
  - Gemfile
@@ -83,12 +140,12 @@ files:
83
140
  - lib/vrbo.rb
84
141
  - lib/vrbo/availability.rb
85
142
  - lib/vrbo/calendar.rb
86
- - lib/vrbo/class_methods.rb
87
143
  - lib/vrbo/configuration.rb
88
144
  - lib/vrbo/version.rb
89
- - test/support/calendar_dates.rb
90
- - test/vrbo/availability_test.rb
91
- - test/vrbo/calendar_test.rb
145
+ - spec/fixtures/vcr_cassettes/293021.yml
146
+ - spec/spec_helper.rb
147
+ - spec/vrbo/availability_spec.rb
148
+ - spec/vrbo/calendar_spec.rb
92
149
  - vrbo.gemspec
93
150
  homepage: ''
94
151
  licenses:
@@ -115,6 +172,7 @@ signing_key:
115
172
  specification_version: 4
116
173
  summary: Scrapes a VRBO calendar using Mechanize
117
174
  test_files:
118
- - test/support/calendar_dates.rb
119
- - test/vrbo/availability_test.rb
120
- - test/vrbo/calendar_test.rb
175
+ - spec/fixtures/vcr_cassettes/293021.yml
176
+ - spec/spec_helper.rb
177
+ - spec/vrbo/availability_spec.rb
178
+ - spec/vrbo/calendar_spec.rb
@@ -1,14 +0,0 @@
1
- module VRBO
2
- module ClassMethods
3
-
4
- def method_missing(method_name, *arguments)
5
- inst = new
6
- if inst.respond_to?(method_name)
7
- inst.send(method_name, *arguments)
8
- else
9
- super
10
- end
11
- end
12
-
13
- end
14
- end
@@ -1,47 +0,0 @@
1
- module CalendarDates
2
-
3
- def available?(list, a = today, b = tomorrow)
4
- vrbo_calendar.available?(a, b, list)
5
- end
6
-
7
- def all
8
- prep [today, tomorrow]
9
- end
10
-
11
- def random
12
- prep all.shuffle
13
- end
14
-
15
- def arrival_only
16
- prep [today]
17
- end
18
-
19
- def depart_only
20
- prep [tomorrow]
21
- end
22
-
23
- def prep(list)
24
- list.map(&:to_s)
25
- end
26
-
27
- def today
28
- @today ||= Date.today
29
- end
30
-
31
- def tomorrow
32
- @tomorrow ||= today + 1
33
- end
34
-
35
- def vrbo_calendar
36
- @vrbo_calendar ||= begin
37
- calendar = VRBO::Calendar.new
38
-
39
- def calendar.collect_days_for_month(date)
40
- dates = date.upto(date.next_month).to_a
41
- dates.map(&:day).map(&:to_s).take(3)
42
- end
43
-
44
- calendar
45
- end
46
- end
47
- end
@@ -1,21 +0,0 @@
1
- $: << 'test'
2
- require 'minitest/autorun'
3
- require 'vrbo'
4
- require 'support/calendar_dates'
5
-
6
- class AvailabilityTest < MiniTest::Unit::TestCase
7
- include CalendarDates
8
-
9
- def test_new_with_params
10
- availability = VRBO::Availability.new(all << (tomorrow + 2).to_s)
11
- assert_equal today, availability.start_at
12
- assert_equal 2, availability.duration
13
- end
14
-
15
- def test_new_without_params
16
- availability = VRBO::Availability.new
17
- assert_equal today, availability.start_at
18
- assert_equal 1, availability.duration
19
- end
20
-
21
- end
@@ -1,70 +0,0 @@
1
- $: << 'test'
2
- require 'minitest/autorun'
3
- require 'vrbo'
4
- require 'support/calendar_dates'
5
-
6
- class CalendarTest < MiniTest::Unit::TestCase
7
- include CalendarDates
8
-
9
- def calendar_id
10
- 212121
11
- end
12
-
13
- def test_available_with_all
14
- assert_equal true, available?(all)
15
- end
16
-
17
- def test_available_with_random
18
- assert_equal true, available?(random)
19
- end
20
-
21
- def test_available_with_arrival
22
- assert_equal true, available?(arrival_only)
23
- end
24
-
25
- def test_available_with_depart
26
- assert_equal false, available?(depart_only)
27
- end
28
-
29
- def test_available_with_empty
30
- assert_equal false, available?([])
31
- end
32
-
33
- def test_available_with_edge
34
- assert_equal true, available?(all, today, tomorrow + 1)
35
- end
36
-
37
- def test_available_with_exceeding
38
- assert_equal false, available?(all, today, tomorrow + 2)
39
- end
40
-
41
- def test_find_all_available_dates
42
- # 3 days taken for each month (36)
43
- assert_equal 36, vrbo_calendar.find_available_dates.length
44
- assert_equal 12, vrbo_calendar.days.length
45
- assert_equal today.to_s, vrbo_calendar.available_dates.first
46
- end
47
-
48
- def test_passing_calendar_id
49
- calendar = VRBO::Calendar.new(calendar_id)
50
- assert_equal calendar_id, calendar.id
51
- assert_equal "http://www.vrbo.com/#{calendar_id}/calendar", calendar.url
52
- assert_equal "https://www.vrbo.com/#{calendar_id}/calendar", calendar.url('https')
53
- end
54
-
55
- def test_no_calendar_id_given
56
- assert_raises ArgumentError do
57
- VRBO::Calendar.new.url
58
- end
59
- end
60
-
61
- def test_calendar_id_from_config
62
- VRBO.configure do |config|
63
- config.calendar_id = calendar_id
64
- end
65
- calendar = VRBO::Calendar.new
66
- VRBO.reset_config
67
- assert_equal calendar_id, calendar.id
68
- assert_equal "http://www.vrbo.com/#{calendar_id}/calendar", calendar.url
69
- end
70
- end