ovchipkaart 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 +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
|
+
[![Coverage Status](https://coveralls.io/repos/JanDintel/ovchipkaart/badge.png)](https://coveralls.io/r/JanDintel/ovchipkaart)
|
4
|
+
[![Build Status](https://travis-ci.org/JanDintel/ovchipkaart.png?branch=master)](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
|