ovchipkaart 0.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 +7 -0
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +22 -0
- data/README.md +107 -0
- data/Rakefile +7 -0
- data/lib/ovchipkaart.rb +51 -0
- data/lib/ovchipkaart/api.rb +59 -0
- data/lib/ovchipkaart/parser.rb +64 -0
- data/lib/ovchipkaart/scraper.rb +68 -0
- data/lib/ovchipkaart/setup.rb +22 -0
- data/lib/ovchipkaart/version.rb +4 -0
- data/ovchipkaart.gemspec +31 -0
- data/spec/factories/api_factory.rb +33 -0
- data/spec/factories/csv_factory.rb +6 -0
- data/spec/fixtures/invalid_configuration.yml +2 -0
- data/spec/fixtures/test_transaction_1.csv +14 -0
- data/spec/lib/ovchipkaart/api_spec.rb +30 -0
- data/spec/lib/ovchipkaart/parser_spec.rb +63 -0
- data/spec/lib/ovchipkaart/scraper_spec.rb +70 -0
- data/spec/lib/ovchipkaart_spec.rb +41 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/ovchipkaart_configuration_support.rb +1 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 09ccdb1d5717c059077262bbf71be5c193b0224c
|
4
|
+
data.tar.gz: 952b7c38c40a842f689bc4ebcf82c357681e15f1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a9039725bc5bda89975e6bf04990265d17e22481e800661a6d0e2e981283ad052a77036f066eca264f287190165df9510c83576347df1563aecf597d79956da8
|
7
|
+
data.tar.gz: 10b222ac46cee284ae09c48b7510a246185d70072c3ec56e7bad50fafa8ddbfd74d83c8c837c59f7ca8fab643654ff0d55474572bc60387a6d5b5dc1a8bcd894
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem 'capybara'
|
6
|
+
gem 'selenium-webdriver'
|
7
|
+
gem 'chromedriver-helper', github: 'mars/chromedriver-helper', ref: '3be3d'
|
8
|
+
|
9
|
+
gem 'coveralls', require: false
|
10
|
+
|
11
|
+
group :development, :test do
|
12
|
+
gem 'pry', require: false
|
13
|
+
gem 'rspec'
|
14
|
+
end
|
15
|
+
|
16
|
+
group :test do
|
17
|
+
gem 'rake'
|
18
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Jan van der Pas
|
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,107 @@
|
|
1
|
+
# OV Chipkaart
|
2
|
+
|
3
|
+
[](https://coveralls.io/r/JanDintel/ovchipkaart)
|
4
|
+
[](https://travis-ci.org/JanDintel/ovchipkaart)
|
5
|
+
|
6
|
+
An OV Chipkaart gem, which scrapes the OV Chipkaart website and provides an easy to use API.
|
7
|
+
|
8
|
+
The OV Chipkaart is the Dutch national transportation card for public transport. The use and transactions are regulated by the TLS.
|
9
|
+
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
The OV Chipkaart gem makes use of the [chromedriver](https://code.google.com/p/chromedriver/) in combination with [capybara](https://github.com/jnicklas/capybara) and [selenium](https://github.com/SeleniumHQ/selenium) under the hood.
|
14
|
+
|
15
|
+
For users with homebrew:
|
16
|
+
|
17
|
+
$ brew install chromedriver
|
18
|
+
|
19
|
+
|
20
|
+
Add the gem to your Gemfile:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'ovchipkaart'
|
24
|
+
```
|
25
|
+
|
26
|
+
You might need to update the chromedriver after you ran `bundle install`:
|
27
|
+
|
28
|
+
$ chromedriver-update
|
29
|
+
|
30
|
+
*Chromedriver update with thanks to [flavorjones/chromedriver-helper](https://github.com/flavorjones/chromedriver-helper) and [mars/chromedriver-helper](https://github.com/flavorjones/chromedriver-helper/pull/9).*
|
31
|
+
|
32
|
+
## Configuration
|
33
|
+
|
34
|
+
You need to provide your credentials from the OV Chipkaart website. You can use a hash or a YAML file:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
Ovchipkaart.configure({ username: 'foo', password: 'bar'})
|
38
|
+
|
39
|
+
Ovchipkaart.configure_with(path_to_yaml_file)
|
40
|
+
Ovchipkaart.configure_with('config/credentials.yml')
|
41
|
+
```
|
42
|
+
|
43
|
+
*Don't forget to put this in your .gitignore*
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
Because the TLS doesn't provide an API for the OV Chipkaart, it scrapes the website and downloads the transactions to the filesystem for parsing. Therefore some methods might feel slow.
|
48
|
+
|
49
|
+
**Balance and timestamp**
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
ov_chipkaart = Ovchipkaart::Api.new
|
53
|
+
ov_chipkaart.balance #=> € 45,30
|
54
|
+
ov_chipkaart.last_updated #=> (12-10-2013 20:57)
|
55
|
+
```
|
56
|
+
|
57
|
+
**Journeys and transactions**
|
58
|
+
|
59
|
+
Every journey or transaction is a hash with ten keys: `datum`, `checkin`, `vertrek`, `checkuit`, `bestemming`, `bedrag`, `transactie`, `klasse`, `product` and `opmerkingen`.
|
60
|
+
|
61
|
+
Based on the `transactie` key everything is sorted in six categories:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
ov_chipkaart = Ovchipkaart::Api.new
|
65
|
+
```
|
66
|
+
|
67
|
+
1. `ov_chipkaart.journeys` contains all the finish journeys.
|
68
|
+
2. `ov_chipkaart.checkins` contains all the checkins, when the journey starts.
|
69
|
+
3. `ov_chipkaart.forgotten_checkouts` contains all the journeys that started, but didn't end, because there was no `checkuit`. These can be used to claim your money back.
|
70
|
+
4. `ov_chipkaart.additions` contains all the additions to the balance of the OV Chipkaart.
|
71
|
+
5. `ov_chipkaart.products` contains all the additions of products to the OV Chipkaart, such as subscriptions and discounts.
|
72
|
+
6. `ov_chipkaart.unclassified` contains everything that couldn't be specified.
|
73
|
+
|
74
|
+
## Troubleshooting
|
75
|
+
|
76
|
+
**Credentials**
|
77
|
+
|
78
|
+
The OV Chipkaart gem needs your credentials to login on the OV Chipkaart website.
|
79
|
+
|
80
|
+
If you have trouble configuring your credentials check the YAML for syntax errors and make sure that you only specify an `username` and `password`. Otherwise it will raise an error.
|
81
|
+
|
82
|
+
**Chromedriver**
|
83
|
+
|
84
|
+
Make sure you have installed the latest `chromedriver`. You can use homebrew to easily install the `chromedriver`. You might try running the `chromedriver-update` command, which downloads the helper for the `chromedriver` that is used by `selenium` with `capybara`.
|
85
|
+
|
86
|
+
The OV Chipkaart gem current uses `chromedriver` version `2.8.241036`.
|
87
|
+
|
88
|
+
**OV Chipkaart results**
|
89
|
+
|
90
|
+
Since this gem relies on scraping the OV Chipkaart website, it might break during an update of the website. If you experience this issue either help out with a Pull request or send me a message.
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
|
94
|
+
**TODO/IDEAS:**
|
95
|
+
|
96
|
+
1. Clean up the downloaded files if they are too old
|
97
|
+
2. Add configuration option for the webdriver. (Firefox and PhantonJS)
|
98
|
+
3. Add configuration option for the download path
|
99
|
+
4. Encrypt the credentials and decrypt them in runtime with an OTP.
|
100
|
+
|
101
|
+
**Pull requests:**
|
102
|
+
|
103
|
+
1. Fork it
|
104
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
105
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
106
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
107
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/ovchipkaart.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'ovchipkaart/setup'
|
3
|
+
require 'ovchipkaart/version'
|
4
|
+
require 'ovchipkaart/scraper'
|
5
|
+
require 'ovchipkaart/parser'
|
6
|
+
require 'ovchipkaart/api'
|
7
|
+
|
8
|
+
module Ovchipkaart
|
9
|
+
class OvchipkaartError < StandardError; end
|
10
|
+
class ConfigurationError < OvchipkaartError; end
|
11
|
+
|
12
|
+
# Configuration
|
13
|
+
MANDATORY_CONFIGURATION = { username: nil, password: nil }
|
14
|
+
CONFIGURATIONS_KEYS = MANDATORY_CONFIGURATION.merge!({})
|
15
|
+
|
16
|
+
# Global config hash
|
17
|
+
@config = {}
|
18
|
+
def self.config
|
19
|
+
@config
|
20
|
+
end
|
21
|
+
|
22
|
+
# Configuration with a hash
|
23
|
+
def self.configure(options = {})
|
24
|
+
MANDATORY_CONFIGURATION.each_key do |mandatory_option|
|
25
|
+
unless options.keys.map(&:to_sym).include?(mandatory_option)
|
26
|
+
raise ConfigurationError, 'Username and password are mandatory configuration options'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
options.keys.map(&:to_sym).each do |given_configuration_key|
|
31
|
+
unless CONFIGURATIONS_KEYS.keys.include? given_configuration_key
|
32
|
+
raise ConfigurationError, 'Unknown configuration given'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
options.each { |key, value| @config[key.to_sym] = value }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Configuration with a YAML file
|
40
|
+
def self.configure_with(path_to_yaml_file)
|
41
|
+
begin
|
42
|
+
config = YAML::load(IO.read(path_to_yaml_file))
|
43
|
+
rescue Errno::ENOENT
|
44
|
+
raise ConfigurationError, 'YAML configuration file not found'
|
45
|
+
rescue Psych::SyntaxError
|
46
|
+
raise ConfigurationError, 'YAML configuration file contains invalid syntax'
|
47
|
+
end
|
48
|
+
|
49
|
+
configure(config)
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Ovchipkaart
|
3
|
+
class Api
|
4
|
+
attr_reader :parser
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@parser = parse_transactions
|
8
|
+
end
|
9
|
+
|
10
|
+
def balance
|
11
|
+
balance_and_date[:balance]
|
12
|
+
end
|
13
|
+
|
14
|
+
def last_updated
|
15
|
+
balance_and_date[:date]
|
16
|
+
end
|
17
|
+
|
18
|
+
def journeys
|
19
|
+
parser.journeys
|
20
|
+
end
|
21
|
+
|
22
|
+
def checkins
|
23
|
+
parser.check_ins
|
24
|
+
end
|
25
|
+
|
26
|
+
def forgotten_checkouts
|
27
|
+
parser.forgotten_check_outs
|
28
|
+
end
|
29
|
+
|
30
|
+
def additions
|
31
|
+
parser.additions
|
32
|
+
end
|
33
|
+
|
34
|
+
def products
|
35
|
+
parser.products
|
36
|
+
end
|
37
|
+
|
38
|
+
def unclassified
|
39
|
+
parser.others
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def balance_and_date
|
45
|
+
regex = scraper.balance.match(/(€.*)(\(.*\))/)
|
46
|
+
{balance: regex[1].strip, date: regex[2]}
|
47
|
+
end
|
48
|
+
|
49
|
+
def scraper
|
50
|
+
@scraper ||= Scraper.scrape
|
51
|
+
end
|
52
|
+
|
53
|
+
def parse_transactions
|
54
|
+
parser = Parser.new
|
55
|
+
parser.sort_csv_file
|
56
|
+
@parser ||= parser
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
module Ovchipkaart
|
5
|
+
class Parser
|
6
|
+
attr_reader :journeys, :check_ins, :additions, :others, :products, :forgotten_check_outs
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@journeys = []
|
10
|
+
@check_ins = []
|
11
|
+
@additions = []
|
12
|
+
@others = []
|
13
|
+
@products = []
|
14
|
+
@forgotten_check_outs = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.process_transactions
|
18
|
+
new.sort_csv_file
|
19
|
+
end
|
20
|
+
|
21
|
+
def sort_csv_file
|
22
|
+
transactions_enum = transactions.each
|
23
|
+
begin
|
24
|
+
transaction = transactions_enum.next
|
25
|
+
case transaction[:transactie]
|
26
|
+
when "Reis"
|
27
|
+
journeys << transaction
|
28
|
+
when "Check-in"
|
29
|
+
if transactions_enum.peek[:transactie] == "Check-in"
|
30
|
+
forgotten_check_outs << transaction
|
31
|
+
else
|
32
|
+
check_ins << transaction
|
33
|
+
end
|
34
|
+
when "Saldo opgeladen"
|
35
|
+
additions << transaction
|
36
|
+
when "Product op kaart gezet"
|
37
|
+
products << transaction
|
38
|
+
else
|
39
|
+
others << transaction
|
40
|
+
end
|
41
|
+
rescue StopIteration
|
42
|
+
break
|
43
|
+
end while true
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_csv_file
|
47
|
+
CSV.new(csv_file, headers: true, header_converters: :symbol, col_sep: ';')
|
48
|
+
end
|
49
|
+
|
50
|
+
def csv_file
|
51
|
+
File.read last_downloaded_file
|
52
|
+
end
|
53
|
+
|
54
|
+
def last_downloaded_file
|
55
|
+
Dir['tmp/downloads/*'].sort_by{ |f| File.basename(f) }.first
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def transactions
|
61
|
+
@transactions ||= parse_csv_file.to_a.map { |row| row.to_hash }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Ovchipkaart
|
3
|
+
class Scraper
|
4
|
+
include Capybara::DSL
|
5
|
+
|
6
|
+
attr_reader :balance
|
7
|
+
|
8
|
+
def self.scrape
|
9
|
+
scraper = new
|
10
|
+
scraper.visit_ovchipkaart
|
11
|
+
scraper.login_user
|
12
|
+
scraper.visit_transaction_overview
|
13
|
+
scraper.select_transaction_period
|
14
|
+
scraper.checkbox_all_transactions
|
15
|
+
scraper.download_transaction_history
|
16
|
+
scraper.find_balance
|
17
|
+
scraper
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_ovchipkaart
|
21
|
+
visit('https://www.ov-chipkaart.nl/login/')
|
22
|
+
accept_cookies if need_to_accept_cookies?
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def login_user
|
27
|
+
fill_in 'gebruikersnaam', with: Ovchipkaart.config[:username]
|
28
|
+
fill_in 'wachtwoord', with: Ovchipkaart.config[:password]
|
29
|
+
click_button 'Inloggen'
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def visit_transaction_overview
|
34
|
+
click_link 'Transactieoverzicht'
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def select_transaction_period
|
39
|
+
select('2013', from: 'periodes')
|
40
|
+
click_button 'Transacties tonen'
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def checkbox_all_transactions
|
45
|
+
check('transactiePanel:container:transacties.declareerAlles')
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def download_transaction_history
|
50
|
+
click_link 'Opslaan als CSV'
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def find_balance
|
55
|
+
@balance = all('span', text: /[€,\d]/)[5].text
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def need_to_accept_cookies?
|
61
|
+
!find_link('Ik accepteer deze cookies').nil? rescue Capybara::ElementNotFound
|
62
|
+
end
|
63
|
+
|
64
|
+
def accept_cookies
|
65
|
+
click_on('Ik accepteer deze cookies') rescue Capybara::ElementNotFound
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Ovchipkaart
|
3
|
+
module Setup
|
4
|
+
# YAML Configuration
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
# Capybara
|
8
|
+
require 'capybara'
|
9
|
+
require 'capybara/dsl'
|
10
|
+
require 'selenium/webdriver'
|
11
|
+
|
12
|
+
Capybara.register_driver :selenium_chrome do |app|
|
13
|
+
preferences = {"download" => {"default_directory" => Dir.getwd + '/tmp/downloads', "directory_upgrade" => true, "extensions_to_open" => ""}}
|
14
|
+
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
|
15
|
+
capabilities['chromeOptions'] = {'prefs' => preferences}
|
16
|
+
Capybara::Selenium::Driver.new(app, :detach => false, :browser => :chrome, :desired_capabilities => capabilities)
|
17
|
+
end
|
18
|
+
|
19
|
+
Capybara.default_driver = :selenium_chrome
|
20
|
+
Capybara.run_server = false
|
21
|
+
end
|
22
|
+
end
|
data/ovchipkaart.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'ovchipkaart/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'ovchipkaart'
|
9
|
+
spec.version = Ovchipkaart::VERSION
|
10
|
+
spec.authors = ['Jan van der Pas']
|
11
|
+
spec.email = ['janvanderpas@gmail.com']
|
12
|
+
spec.description = "An unofficial OV Chipkaart gem, which scrapes the OV Chipkaart website (ov-chipkaart.nl) and provides an easy to use Ruby API."
|
13
|
+
spec.summary = 'Unofficial Ruby OV Chipkaart API gem'
|
14
|
+
spec.homepage = ''
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.requirements << 'chromedriver, v2.8'
|
23
|
+
spec.post_install_message = "Make sure that the chromedriver is installed before using the ovchipkaart gem."
|
24
|
+
|
25
|
+
spec.add_runtime_dependency 'nokogiri', '~> 1.6.0'
|
26
|
+
spec.add_runtime_dependency 'capybara', '~> 2.2.0'
|
27
|
+
spec.add_runtime_dependency 'selenium-webdriver', '~> 2.38.0'
|
28
|
+
|
29
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
30
|
+
spec.add_development_dependency 'rake'
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Factory
|
2
|
+
|
3
|
+
def self.journeys
|
4
|
+
[{:datum=>"13-12-2013", :checkin=>"", :vertrek=>"Grote Markt", :checkuit=>"19:57", :bestemming=>"Muurbloemweg", :bedrag=>"1,76", :transactie=>"Reis", :klasse=>"", :product=>"", :opmerkingen=>""},
|
5
|
+
{:datum=>"13-12-2013", :checkin=>"", :vertrek=>"Leiden Centraal", :checkuit=>"09:20", :bestemming=>"Haag HS (Den)", :bedrag=>"1,90", :transactie=>"Reis", :klasse=>"2", :product=>"Altijd Voordeel (maandabonnement)", :opmerkingen=>""},
|
6
|
+
{:datum=>"12-12-2013", :checkin=>"", :vertrek=>"Haag HS (Den)", :checkuit=>"18:30", :bestemming=>"Leiden Centraal", :bedrag=>"2,60", :transactie=>"Reis", :klasse=>"2", :product=>"Altijd Voordeel (maandabonnement)", :opmerkingen=>""},
|
7
|
+
{:datum=>"12-12-2013", :checkin=>"", :vertrek=>"Leiden Centraal", :checkuit=>"09:21", :bestemming=>"Haag HS (Den)", :bedrag=>"1,90", :transactie=>"Reis", :klasse=>"2", :product=>"Altijd Voordeel (maandabonnement)", :opmerkingen=>""}]
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.checkins
|
11
|
+
[{:datum=>"13-12-2013", :checkin=>"19:38", :vertrek=>"Grote Markt", :checkuit=>"", :bestemming=>"", :bedrag=>"-4,00", :transactie=>"Check-in", :klasse=>"", :product=>"", :opmerkingen=>""},
|
12
|
+
{:datum=>"13-12-2013", :checkin=>"08:57", :vertrek=>"Leiden Centraal", :checkuit=>"", :bestemming=>"", :bedrag=>"-10,00", :transactie=>"Check-in", :klasse=>"", :product=>"", :opmerkingen=>""},
|
13
|
+
{:datum=>"12-12-2013", :checkin=>"18:10", :vertrek=>"Haag HS (Den)", :checkuit=>"", :bestemming=>"", :bedrag=>"-10,00", :transactie=>"Check-in", :klasse=>"", :product=>"", :opmerkingen=>""},
|
14
|
+
{:datum=>"12-12-2013", :checkin=>"08:55", :vertrek=>"Leiden Centraal", :checkuit=>"", :bestemming=>"", :bedrag=>"-10,00", :transactie=>"Check-in", :klasse=>"", :product=>"", :opmerkingen=>""},
|
15
|
+
{:datum=>"11-12-2013", :checkin=>"8:10", :vertrek=>"Leiden Centraal", :checkuit=>"", :bestemming=>"", :bedrag=>"-10,00", :transactie=>"Check-in", :klasse=>"", :product=>"", :opmerkingen=>""}]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.forgotten_checkouts
|
19
|
+
[{:datum=>"11-12-2013", :checkin=>"8:15", :vertrek=>"Den Haag Laan van NOI", :checkuit=>"", :bestemming=>"", :bedrag=>"-10,00", :transactie=>"Check-in", :klasse=>"", :product=>"", :opmerkingen=>""}]
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.additions
|
23
|
+
[{:datum=>"12-12-2013", :checkin=>"08:52", :vertrek=>"", :checkuit=>"", :bestemming=>"", :bedrag=>"50,00", :transactie=>"Saldo opgeladen", :klasse=>"", :product=>"", :opmerkingen=>""}]
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.products
|
27
|
+
[{:datum=>"10-12-2013", :checkin=>"8:00", :vertrek=>"", :checkuit=>"", :bestemming=>"", :bedrag=>"", :transactie=>"Product op kaart gezet", :klasse=>"2", :product=>"Altijd Voordeel (maandabonnement)", :opmerkingen=>""}]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.unclassified
|
31
|
+
[{:datum=>"9-12-2013", :checkin=>"7:00", :vertrek=>"", :checkuit=>"", :bestemming=>"", :bedrag=>"", :transactie=>"Foo", :klasse=>"", :product=>"", :opmerkingen=>""}]
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
"Datum";"Check-in";"Vertrek";"Check-uit";"Bestemming";"Bedrag";"Transactie";"Klasse";"Product";"Opmerkingen"
|
2
|
+
"13-12-2013";"";"Grote Markt";"19:57";"Muurbloemweg";"1,76";"Reis";"";"";""
|
3
|
+
"13-12-2013";"19:38";"Grote Markt";"";"";"-4,00";"Check-in";"";"";""
|
4
|
+
"13-12-2013";"";"Leiden Centraal";"09:20";"Haag HS (Den)";"1,90";"Reis";"2";"Altijd Voordeel (maandabonnement)";""
|
5
|
+
"13-12-2013";"08:57";"Leiden Centraal";"";"";"-10,00";"Check-in";"";"";""
|
6
|
+
"12-12-2013";"";"Haag HS (Den)";"18:30";"Leiden Centraal";"2,60";"Reis";"2";"Altijd Voordeel (maandabonnement)";""
|
7
|
+
"12-12-2013";"18:10";"Haag HS (Den)";"";"";"-10,00";"Check-in";"";"";""
|
8
|
+
"12-12-2013";"";"Leiden Centraal";"09:21";"Haag HS (Den)";"1,90";"Reis";"2";"Altijd Voordeel (maandabonnement)";""
|
9
|
+
"12-12-2013";"08:55";"Leiden Centraal";"";"";"-10,00";"Check-in";"";"";""
|
10
|
+
"12-12-2013";"08:52";"";"";"";"50,00";"Saldo opgeladen";"";"";""
|
11
|
+
"11-12-2013";"8:15";"Den Haag Laan van NOI";"";"";"-10,00";"Check-in";"";"";""
|
12
|
+
"11-12-2013";"8:10";"Leiden Centraal";"";"";"-10,00";"Check-in";"";"";""
|
13
|
+
"10-12-2013";"8:00";"";"";"";"";"Product op kaart gezet";"2";"Altijd Voordeel (maandabonnement)";""
|
14
|
+
"9-12-2013";"7:00";"";"";"";"";"Foo";"";"";""
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Ovchipkaart::Api do
|
5
|
+
let(:csv_file) { Factory.csv_file }
|
6
|
+
let(:journeys) { Factory.journeys }
|
7
|
+
let(:checkins) { Factory.checkins }
|
8
|
+
let(:forgotten_checkouts) { Factory.forgotten_checkouts }
|
9
|
+
let(:additions) { Factory.additions }
|
10
|
+
let(:products) { Factory.products }
|
11
|
+
let(:unclassified) { Factory.unclassified }
|
12
|
+
|
13
|
+
describe 'public API' do
|
14
|
+
before { Ovchipkaart::Parser.any_instance.stub(:csv_file).and_return csv_file }
|
15
|
+
|
16
|
+
specify { expect(subject.journeys).to eql journeys }
|
17
|
+
specify { expect(subject.checkins).to eql checkins }
|
18
|
+
specify { expect(subject.forgotten_checkouts).to eql forgotten_checkouts }
|
19
|
+
specify { expect(subject.additions).to eql additions }
|
20
|
+
specify { expect(subject.products).to eql products }
|
21
|
+
specify { expect(subject.unclassified).to eql unclassified }
|
22
|
+
|
23
|
+
context 'with web_driver', :web_driver do
|
24
|
+
before { Ovchipkaart::Scraper.any_instance.stub(:balance).and_return "€ 30,51 (19-12-2013 20:57)" }
|
25
|
+
|
26
|
+
specify { expect(subject.balance).to eql '€ 30,51' }
|
27
|
+
specify { expect(subject.last_updated).to eql '(19-12-2013 20:57)' }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Ovchipkaart::Parser do
|
5
|
+
let(:csv_file) { Factory.csv_file }
|
6
|
+
let(:csv_file_stub) { described_class.any_instance.stub(:csv_file).and_return csv_file }
|
7
|
+
|
8
|
+
describe '.process_transactions' do
|
9
|
+
it 'sorts the transactions' do
|
10
|
+
expect_any_instance_of(described_class).to receive :sort_csv_file
|
11
|
+
described_class.process_transactions
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#sort_csv_file' do
|
16
|
+
before { csv_file_stub }
|
17
|
+
before { subject.sort_csv_file }
|
18
|
+
|
19
|
+
specify { expect(subject.journeys.count).to eql 4 }
|
20
|
+
specify { expect(subject.check_ins.count).to eql 5 }
|
21
|
+
specify { expect(subject.additions.count).to eql 1 }
|
22
|
+
specify { expect(subject.products.count).to eql 1 }
|
23
|
+
specify { expect(subject.others.count).to eql 1 }
|
24
|
+
specify {
|
25
|
+
expect(subject.forgotten_check_outs.count).to eql 1 }
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#parse_csv_file' do
|
29
|
+
before { csv_file_stub }
|
30
|
+
|
31
|
+
it 'parses the csv file' do
|
32
|
+
expect(CSV).to receive(:new).with(csv_file, { headers: true, header_converters: :symbol, col_sep: ";" })
|
33
|
+
subject.parse_csv_file
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'downloaded csv file' do
|
38
|
+
let(:tmp_dir) { "#{Dir.getwd}/tmp/downloads"}
|
39
|
+
let(:file_1_path) { "#{tmp_dir}/transacties_-0001.csv" }
|
40
|
+
let(:file_2_path) { "#{tmp_dir}/transacties_-0002.csv" }
|
41
|
+
|
42
|
+
before do
|
43
|
+
FileUtils.mkdir_p tmp_dir
|
44
|
+
File.open(file_1_path, 'w+') { |file| file.write csv_file }
|
45
|
+
File.new file_2_path, 'w+'
|
46
|
+
end
|
47
|
+
|
48
|
+
after do
|
49
|
+
FileUtils.rm file_1_path
|
50
|
+
FileUtils.rm file_2_path
|
51
|
+
end
|
52
|
+
|
53
|
+
context '#csv_file' do
|
54
|
+
specify { expect(subject.csv_file).to eql csv_file }
|
55
|
+
end
|
56
|
+
|
57
|
+
context '#last_downloaded_file' do
|
58
|
+
it 'sorts the file on their basename and returns first one' do
|
59
|
+
expect(subject.last_downloaded_file).to eql 'tmp/downloads/transacties_-0001.csv'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'capybara/rspec'
|
4
|
+
|
5
|
+
# Methods in the Ovchipkaart::Scraper class are,
|
6
|
+
# dependent on the order of calling them.
|
7
|
+
RSpec.configure { |config| config.order = 'default' }
|
8
|
+
|
9
|
+
describe Ovchipkaart::Scraper, :web_driver do
|
10
|
+
|
11
|
+
describe '.scrape' do
|
12
|
+
it 'scrapes the ovchipkaart website' do
|
13
|
+
expect_any_instance_of(described_class).to receive(:visit_ovchipkaart)
|
14
|
+
expect_any_instance_of(described_class).to receive(:login_user)
|
15
|
+
expect_any_instance_of(described_class).to receive(:visit_transaction_overview)
|
16
|
+
expect_any_instance_of(described_class).to receive(:select_transaction_period)
|
17
|
+
expect_any_instance_of(described_class).to receive(:checkbox_all_transactions)
|
18
|
+
expect_any_instance_of(described_class).to receive(:download_transaction_history)
|
19
|
+
expect_any_instance_of(described_class).to receive(:find_balance)
|
20
|
+
described_class.scrape
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#visit_ovchipkaart' do
|
25
|
+
it 'lands on the login page of ovchipkaart' do
|
26
|
+
expect(subject.visit_ovchipkaart).to have_content 'Inloggen op Mijn OV-chipkaart'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#login_user' do
|
31
|
+
it 'fills out the user credentials and logs in' do
|
32
|
+
expect(subject.login_user).to have_content 'Transactieoverzicht'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#visit_transaction_overview' do
|
37
|
+
it 'visits the transaction overview page' do
|
38
|
+
expect(subject.visit_transaction_overview).to have_content 'Mijn transactieoverzicht'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#select_transaction_period' do
|
43
|
+
it 'changes the time period of the transaction history' do
|
44
|
+
expect(subject.select_transaction_period).to have_select('periodes', selected: '2013')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#checkbox_all_transactions' do
|
49
|
+
it 'checks on all transactions' do
|
50
|
+
expect(subject.checkbox_all_transactions).to have_checked_field('transactiePanel:container:transacties.declareerAlles', checked: true)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#download_transaction_history' do
|
55
|
+
let(:download_path) { Dir.getwd + '/tmp/downloads/*' }
|
56
|
+
|
57
|
+
it 'downloads the transaction history' do
|
58
|
+
# Give time to process download before calling downloads count again
|
59
|
+
expect{ subject.download_transaction_history; sleep 3 }.to change{ Dir[download_path].length }.by(1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#find_balance' do
|
64
|
+
before { subject.find_balance }
|
65
|
+
|
66
|
+
it 'scrapes the balance of the transaction overview page' do
|
67
|
+
expect(subject.balance).to match /(€+\s+\d*,+\d*)+\s*(\(\d*-\d*-\d*\s\d*:\d*\))/
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Ovchipkaart do
|
5
|
+
|
6
|
+
describe 'configuration' do
|
7
|
+
context 'valid configuration' do
|
8
|
+
specify { expect{ subject.configure({ username: 'foo', password: 'bar' }) }.not_to raise_error }
|
9
|
+
|
10
|
+
it 'sets the configuration' do
|
11
|
+
subject.configure({ username: 'foo', password: 'bar' })
|
12
|
+
expect(subject.config[:username]).to eql 'foo'
|
13
|
+
expect(subject.config[:password]).to eql 'bar'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'mandatory configuration' do
|
18
|
+
specify { expect(subject.const_get :MANDATORY_CONFIGURATION).to eql({:username=>nil, :password=>nil}) }
|
19
|
+
|
20
|
+
it 'raises an Ovchipkaart::ConfigurationError without the mandatory configuration' do
|
21
|
+
expect{ subject.configure({ foo: 'bar' }) }.to raise_error Ovchipkaart::ConfigurationError, 'Username and password are mandatory configuration options'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'configuration keys' do
|
26
|
+
it 'raises an Ovchipkaart::ConfigurationError with invalid configuration keys' do
|
27
|
+
expect{ subject.configure({ username: 'foo', password: 'bar', baz: 'foobar' }) }.to raise_error Ovchipkaart::ConfigurationError, 'Unknown configuration given'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'YAML configuration' do
|
32
|
+
it 'raises an Ovchipkaart::ConfigurationError without a YAML file' do
|
33
|
+
expect{ subject.configure_with('') }.to raise_error Ovchipkaart::ConfigurationError, 'YAML configuration file not found'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'raises an Ovchipkaart::ConfigurationError with an invalid YAML file' do
|
37
|
+
expect{ subject.configure_with('spec/fixtures/invalid_configuration.yml') }.to raise_error Ovchipkaart::ConfigurationError, 'YAML configuration file contains invalid syntax'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'coveralls'
|
3
|
+
|
4
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
5
|
+
if ENV['TRAVIS_CI'].nil?
|
6
|
+
SimpleCov.start
|
7
|
+
else
|
8
|
+
SimpleCov.start do
|
9
|
+
add_filter 'lib/ovchipkaart/scraper.rb'
|
10
|
+
add_filter 'spec/lib/ovchipkaart/scraper_spec.rb'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'ovchipkaart'
|
15
|
+
|
16
|
+
# Add support files
|
17
|
+
Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each { |file| require file }
|
18
|
+
|
19
|
+
# Add factories
|
20
|
+
Dir[File.join(File.dirname(__FILE__), "factories/**/*.rb")].each { |file| require file }
|
21
|
+
|
22
|
+
RSpec.configure do |config|
|
23
|
+
config.expect_with :rspec do |c|
|
24
|
+
c.syntax = :expect
|
25
|
+
end
|
26
|
+
|
27
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
28
|
+
|
29
|
+
config.order = 'random'
|
30
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Ovchipkaart.configure_with('spec/support/credentials.yml') if ENV['TRAVIS_CI'].nil?
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ovchipkaart
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jan van der Pas
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.6.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.6.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: capybara
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.2.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: selenium-webdriver
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.38.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.38.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: An unofficial OV Chipkaart gem, which scrapes the OV Chipkaart website
|
84
|
+
(ov-chipkaart.nl) and provides an easy to use Ruby API.
|
85
|
+
email:
|
86
|
+
- janvanderpas@gmail.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- .gitignore
|
92
|
+
- .rspec
|
93
|
+
- .travis.yml
|
94
|
+
- Gemfile
|
95
|
+
- LICENSE.txt
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- lib/ovchipkaart.rb
|
99
|
+
- lib/ovchipkaart/api.rb
|
100
|
+
- lib/ovchipkaart/parser.rb
|
101
|
+
- lib/ovchipkaart/scraper.rb
|
102
|
+
- lib/ovchipkaart/setup.rb
|
103
|
+
- lib/ovchipkaart/version.rb
|
104
|
+
- ovchipkaart.gemspec
|
105
|
+
- spec/factories/api_factory.rb
|
106
|
+
- spec/factories/csv_factory.rb
|
107
|
+
- spec/fixtures/invalid_configuration.yml
|
108
|
+
- spec/fixtures/test_transaction_1.csv
|
109
|
+
- spec/lib/ovchipkaart/api_spec.rb
|
110
|
+
- spec/lib/ovchipkaart/parser_spec.rb
|
111
|
+
- spec/lib/ovchipkaart/scraper_spec.rb
|
112
|
+
- spec/lib/ovchipkaart_spec.rb
|
113
|
+
- spec/spec_helper.rb
|
114
|
+
- spec/support/ovchipkaart_configuration_support.rb
|
115
|
+
homepage: ''
|
116
|
+
licenses:
|
117
|
+
- MIT
|
118
|
+
metadata: {}
|
119
|
+
post_install_message: Make sure that the chromedriver is installed before using the
|
120
|
+
ovchipkaart gem.
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements:
|
135
|
+
- chromedriver, v2.8
|
136
|
+
rubyforge_project:
|
137
|
+
rubygems_version: 2.1.11
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: Unofficial Ruby OV Chipkaart API gem
|
141
|
+
test_files:
|
142
|
+
- spec/factories/api_factory.rb
|
143
|
+
- spec/factories/csv_factory.rb
|
144
|
+
- spec/fixtures/invalid_configuration.yml
|
145
|
+
- spec/fixtures/test_transaction_1.csv
|
146
|
+
- spec/lib/ovchipkaart/api_spec.rb
|
147
|
+
- spec/lib/ovchipkaart/parser_spec.rb
|
148
|
+
- spec/lib/ovchipkaart/scraper_spec.rb
|
149
|
+
- spec/lib/ovchipkaart_spec.rb
|
150
|
+
- spec/spec_helper.rb
|
151
|
+
- spec/support/ovchipkaart_configuration_support.rb
|