holiday_list 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +46 -0
- data/Rakefile +47 -0
- data/holiday_list.gemspec +33 -0
- data/lib/holiday_list.rb +61 -0
- data/lib/holiday_list/configuration.rb +11 -0
- data/lib/holiday_list/google_calendar_request_string.rb +39 -0
- data/lib/holiday_list/params.rb +60 -0
- data/lib/holiday_list/request_exception.rb +12 -0
- data/lib/holiday_list/version.rb +3 -0
- data/spec/holiday_list/configuration_spec.rb +40 -0
- data/spec/holiday_list/google_calendar_request_string_spec.rb +47 -0
- data/spec/holiday_list/params_spec.rb +107 -0
- data/spec/holiday_list/request_exception_spec.rb +41 -0
- data/spec/holiday_list_spec.rb +83 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/vcr/cassettes/01_29_2014_list.yml +640 -0
- data/spec/vcr/cassettes/bad_google_access_key.yml +59 -0
- metadata +227 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a4897c6155de7143716fe7b0d4d6745fe3c8aef5
|
4
|
+
data.tar.gz: e4664e1f521f868dda8ad81a9b5bf319a8c4e649
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 11209b133401cb0ec369a83500ea0cb44bca517f307082bf8092afcbb93dcf9ca71b98127f0a8117a14d036f1999d5366100a7e8b0e2e0175b709031391ee361
|
7
|
+
data.tar.gz: b7f35acc8298d84e5d57043f48d66d0028361a7f273902de7ba18ee7fcb0d2d2f77c8cb4cbc60becc78d4b522531806da9e1251f0164e3aeda00bd54cca7392c
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
5
|
+
- 2.1.0
|
6
|
+
- jruby-19mode
|
7
|
+
- ruby-head
|
8
|
+
- jruby-head
|
9
|
+
matrix:
|
10
|
+
fast_finish: true
|
11
|
+
allow_failures:
|
12
|
+
- rvm: jruby-head
|
13
|
+
# uncomment this line if your project needs to run something other than `rake`:
|
14
|
+
# script: bundle exec rspec spec
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Ben Hicks
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# Holiday::List
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'holiday_list'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install holiday_list
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
30
|
+
|
31
|
+
## Todo
|
32
|
+
|
33
|
+
- initializer
|
34
|
+
- Travis ci build
|
35
|
+
- Code complete
|
36
|
+
- Test coverage
|
37
|
+
- rename?
|
38
|
+
- clean up readme
|
39
|
+
- ~~replace httparty with faraday~~
|
40
|
+
|
41
|
+
### Its nice to want things
|
42
|
+
|
43
|
+
- connect to more google public calendars
|
44
|
+
- connect to private google calendars
|
45
|
+
- connect to non google calendars
|
46
|
+
- store data in redis
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
require "dotenv"
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new
|
7
|
+
|
8
|
+
task :default => :spec
|
9
|
+
task :test => :spec
|
10
|
+
|
11
|
+
desc 'Launch irb with holiday_list required'
|
12
|
+
task :console do
|
13
|
+
require 'irb'
|
14
|
+
require 'irb/completion'
|
15
|
+
require 'holiday_list'
|
16
|
+
ARGV.clear
|
17
|
+
IRB.start
|
18
|
+
end
|
19
|
+
|
20
|
+
namespace :spec do
|
21
|
+
desc 'Run RSpec, re-recording VCR cassettes'
|
22
|
+
task :rerecord do
|
23
|
+
Dotenv.load
|
24
|
+
|
25
|
+
ENV['RERECORD'] = 'true'
|
26
|
+
Rake::Task['spec'].execute
|
27
|
+
Rake::Task['spec:scrub'].execute
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Scrub access key tokens'
|
31
|
+
task :scrub do
|
32
|
+
Dotenv.load
|
33
|
+
|
34
|
+
FileList.new('spec/vcr/cassettes/*.yml').each do |file|
|
35
|
+
next unless key = ENV['GOOGLE_ACCESS_KEY']
|
36
|
+
|
37
|
+
modified_file = File.open(file).read.gsub(/#{key}/, 'A_GOOD_KEY')
|
38
|
+
|
39
|
+
File.open(file, 'w') do |out|
|
40
|
+
out << modified_file
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# TODO: add git pre-commit hook to prevent users from checking in
|
47
|
+
# GOOGLE_ACCESS_KEY in VCR cassettes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'holiday_list/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "holiday_list"
|
8
|
+
spec.version = HolidayList::VERSION
|
9
|
+
spec.authors = ["Ben Hicks"]
|
10
|
+
spec.email = ["benkhicks@gmail.com"]
|
11
|
+
spec.description = %q{HolidayList}
|
12
|
+
spec.summary = %q{Connects to google calendar and retrieves holidays}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "faraday"
|
22
|
+
spec.add_runtime_dependency "activesupport"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rspec"
|
27
|
+
spec.add_development_dependency "vcr"
|
28
|
+
spec.add_development_dependency "webmock", "~> 1.15.0"
|
29
|
+
spec.add_development_dependency "timecop"
|
30
|
+
spec.add_development_dependency "rubocop"
|
31
|
+
spec.add_development_dependency "dotenv"
|
32
|
+
spec.add_development_dependency "pry"
|
33
|
+
end
|
data/lib/holiday_list.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'holiday_list/version'
|
2
|
+
require 'holiday_list/configuration'
|
3
|
+
require 'holiday_list/google_calendar_request_string'
|
4
|
+
require 'holiday_list/request_exception'
|
5
|
+
require 'faraday'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
# MyList:
|
9
|
+
# Used to generate a list of upcoming holidays
|
10
|
+
class HolidayList
|
11
|
+
include RequestException
|
12
|
+
|
13
|
+
def self.list
|
14
|
+
new.to_a
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.configure
|
18
|
+
yield configuration
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.configuration
|
22
|
+
@configuration ||= Configuration.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@request_string = GoogleCalendarRequestString.new self.class.configuration
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_a
|
30
|
+
argument_error! if invalid_request?
|
31
|
+
|
32
|
+
json_response['items'].map do |item|
|
33
|
+
{
|
34
|
+
summary: item['summary'],
|
35
|
+
start_date: Date.parse(item['start']['date']),
|
36
|
+
etag: item['etag']
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# TODO: methods below are only used in #to_a, and are prime candidates for an extract class refactoring
|
44
|
+
def invalid_request?
|
45
|
+
return false unless response_error
|
46
|
+
response_error['code'] == 400
|
47
|
+
end
|
48
|
+
|
49
|
+
def response
|
50
|
+
conn = Faraday.new url: GoogleCalendarRequestString.url_base
|
51
|
+
conn.get @request_string.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
def json_response
|
55
|
+
@json_response ||= JSON.parse(response.body)
|
56
|
+
end
|
57
|
+
|
58
|
+
def response_error
|
59
|
+
json_response['error']
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'holiday_list/params'
|
2
|
+
require 'holiday_list/request_exception'
|
3
|
+
|
4
|
+
class HolidayList
|
5
|
+
# GoogleCalendarRequestString:
|
6
|
+
# Generates the google calendar api request string
|
7
|
+
class GoogleCalendarRequestString
|
8
|
+
include RequestException
|
9
|
+
|
10
|
+
# TODO: address these items:
|
11
|
+
# - make class more extensible
|
12
|
+
# - rename class, redundat String, and is it really a 'Request'?
|
13
|
+
# - don't think 'to_str' is working. should work like:
|
14
|
+
# gcrs = GoogleCalendarRequestString.new(configuration)
|
15
|
+
# conn.get gcrs # '/calendar/v3/calendars/12345/events?time_min=yesterday'
|
16
|
+
def self.url_base
|
17
|
+
'https://www.googleapis.com'
|
18
|
+
end
|
19
|
+
|
20
|
+
def url_path
|
21
|
+
'/calendar/v3/calendars'
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :id, :params
|
25
|
+
|
26
|
+
def initialize(configuration)
|
27
|
+
argument_error! unless configuration.configured?
|
28
|
+
|
29
|
+
key = configuration.key
|
30
|
+
@id = configuration.id
|
31
|
+
|
32
|
+
@params = Params.new(key)
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
"#{url_path}/#{id}/events?#{params}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'active_support/core_ext/object/to_query'
|
2
|
+
require 'active_support/core_ext/date_time/calculations'
|
3
|
+
|
4
|
+
class HolidayList
|
5
|
+
# Params:
|
6
|
+
# Munges google calendar api request parameters
|
7
|
+
class Params
|
8
|
+
attr_reader :key
|
9
|
+
|
10
|
+
def initialize(key, time_options = {})
|
11
|
+
@key = key
|
12
|
+
@options = time_options
|
13
|
+
|
14
|
+
validate_options!
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
params_hash.to_query
|
19
|
+
end
|
20
|
+
|
21
|
+
def params_hash
|
22
|
+
{
|
23
|
+
key: key,
|
24
|
+
orderBy: order_by,
|
25
|
+
singleEvents: single_events,
|
26
|
+
timeMin: time_min,
|
27
|
+
timeMax: time_max
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def time_min
|
34
|
+
@time_min ||= @options.fetch('start') { DateTime.now.new_offset(0) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def time_max
|
38
|
+
@time_max ||= @options.fetch('stop') { DateTime.now.next_year.new_offset(0) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def order_by
|
42
|
+
'startTime'
|
43
|
+
end
|
44
|
+
|
45
|
+
def single_events
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_options!
|
50
|
+
message = 'Start and end dates must be valid DateTime objects that'\
|
51
|
+
' occur sequentially'
|
52
|
+
|
53
|
+
fail ArgumentError, message if [time_min, time_max].any? do |time|
|
54
|
+
time.class != DateTime
|
55
|
+
end
|
56
|
+
|
57
|
+
fail ArgumentError, message if time_min > time_max
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class HolidayList
|
2
|
+
# RequestException
|
3
|
+
# Provides convenience methods for common exceptions
|
4
|
+
module RequestException
|
5
|
+
private
|
6
|
+
|
7
|
+
def argument_error!(message = nil)
|
8
|
+
message ||= 'A valid google access key is required'
|
9
|
+
fail ArgumentError, message
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HolidayList::Configuration do
|
4
|
+
subject { HolidayList::Configuration.new }
|
5
|
+
|
6
|
+
describe '#configured?' do
|
7
|
+
shared_examples 'failing configuration' do
|
8
|
+
it 'is false' do
|
9
|
+
expect(subject.configured?).to be false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'without a key' do
|
14
|
+
before { subject.id = 'id' }
|
15
|
+
|
16
|
+
it_behaves_like 'failing configuration'
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'without an id' do
|
20
|
+
before { subject.key = 'key' }
|
21
|
+
|
22
|
+
it_behaves_like 'failing configuration'
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'without a key or id' do
|
26
|
+
it_behaves_like 'failing configuration'
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with a key and id' do
|
30
|
+
before do
|
31
|
+
subject.key = 'key'
|
32
|
+
subject.id = 'id'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'is true' do
|
36
|
+
expect(subject.configured?).to be true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HolidayList::GoogleCalendarRequestString do
|
4
|
+
let(:configuration) do
|
5
|
+
double('Configuration', configured?: true).as_null_object
|
6
|
+
end
|
7
|
+
|
8
|
+
subject { HolidayList::GoogleCalendarRequestString.new(configuration) }
|
9
|
+
|
10
|
+
describe '#new' do
|
11
|
+
context 'configured' do
|
12
|
+
before do
|
13
|
+
HolidayList::GoogleCalendarRequestString
|
14
|
+
.any_instance.should_not_receive(:argument_error!)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'creates a new object' do
|
18
|
+
subject
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'not configured' do
|
23
|
+
before do
|
24
|
+
configuration.stub(:configured?).and_return(false)
|
25
|
+
HolidayList::GoogleCalendarRequestString
|
26
|
+
.any_instance.should_receive(:argument_error!)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'calls argument_error!' do
|
30
|
+
subject
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#to_s' do
|
36
|
+
let(:params) { double('Params') }
|
37
|
+
|
38
|
+
before do
|
39
|
+
HolidayList::Params.stub(:new).and_return(params)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'uses Params' do
|
43
|
+
params.should_receive(:to_s)
|
44
|
+
subject.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|