syrup 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.
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/CHANGELOG.rdoc +3 -0
- data/Gemfile +4 -0
- data/README.rdoc +28 -0
- data/Rakefile +11 -0
- data/lib/syrup/account.rb +5 -0
- data/lib/syrup/extract.rb +14 -0
- data/lib/syrup/institutions/abstract_institution.rb +26 -0
- data/lib/syrup/institutions/zions_bank.rb +111 -0
- data/lib/syrup/transaction.rb +10 -0
- data/lib/syrup/version.rb +3 -0
- data/lib/syrup.rb +10 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/syrup/extract_spec.rb +12 -0
- data/spec/syrup/institutions/zions_bank_spec.rb +5 -0
- data/spec/syrup/transaction_spec.rb +20 -0
- data/syrup.gemspec +26 -0
- metadata +109 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/CHANGELOG.rdoc
ADDED
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
= Syrup
|
2
|
+
|
3
|
+
Syrup is made from a yummy maple extract.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
The latest version of Syrup can be installed with Rubygems:
|
8
|
+
|
9
|
+
[sudo] gem install "syrup"
|
10
|
+
|
11
|
+
In <b>Rails 3</b>, add this to your Gemfile and run the +bundle+ command.
|
12
|
+
|
13
|
+
gem "syrup"
|
14
|
+
|
15
|
+
In <b>Rails 2</b>, add this to your environment.rb file.
|
16
|
+
|
17
|
+
config.gem "syrup"
|
18
|
+
|
19
|
+
== Getting Started
|
20
|
+
|
21
|
+
Spread it all over your pancakes and enjoy!
|
22
|
+
|
23
|
+
== Usage
|
24
|
+
|
25
|
+
zions = Syrup::Extract.from_institution(:zions_bank)
|
26
|
+
zions.fetch_accounts.each do |act|
|
27
|
+
puts name + " " + name.current_balance
|
28
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Syrup
|
2
|
+
class Extract
|
3
|
+
def self.from_institution(institution_sym)
|
4
|
+
class_name = institution_sym.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
5
|
+
Institutions.const_get(class_name).new
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.list_institutions
|
9
|
+
Institutions::AbstractInstitution.subclasses.map do |subclass|
|
10
|
+
subclass.institution_name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Syrup
|
2
|
+
module Institutions
|
3
|
+
class AbstractInstitution
|
4
|
+
|
5
|
+
def self.inherited(subclass)
|
6
|
+
@subclasses ||= []
|
7
|
+
@subclasses << subclass
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.subclasses
|
11
|
+
@subclasses
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def agent
|
17
|
+
@agent || Mechanize.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_currency(currency)
|
21
|
+
currency.scan(/[0-9.]/).join.to_f
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Syrup
|
2
|
+
module Institutions
|
3
|
+
class ZionsBank < AbstractInstitution
|
4
|
+
def self.institution_name
|
5
|
+
"Zions Bank"
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_accessor :username, :password, :secret_qas
|
9
|
+
|
10
|
+
def fetch_accounts
|
11
|
+
ensure_authenticated
|
12
|
+
|
13
|
+
# List accounts
|
14
|
+
page = agent.get('https://banking.zionsbank.com/ibuir/displayAccountBalance.htm')
|
15
|
+
json = ActiveSupport::JSON.decode(page.body)
|
16
|
+
|
17
|
+
accounts = []
|
18
|
+
json['accountBalance']['depositAccountList'].each do |account|
|
19
|
+
new_account = Account.new
|
20
|
+
new_account.name = account['name']
|
21
|
+
new_account.id = account['accountId']
|
22
|
+
new_account.account_number = account['number']
|
23
|
+
new_account.current_balance = parse_currency(account['currentAmt'])
|
24
|
+
new_account.available_balance = parse_currency(account['availableAmt'])
|
25
|
+
new_account.type = :deposit
|
26
|
+
|
27
|
+
accounts << new_account
|
28
|
+
end
|
29
|
+
json['accountBalance']['creditAccountList'].each do |account|
|
30
|
+
new_account = Account.new
|
31
|
+
new_account.name = account['name']
|
32
|
+
new_account.id = account['accountId']
|
33
|
+
new_account.account_number = account['number']
|
34
|
+
new_account.current_balance = parse_currency(account['balanceDueAmt'])
|
35
|
+
new_account.type = :credit
|
36
|
+
|
37
|
+
accounts << new_account
|
38
|
+
end
|
39
|
+
|
40
|
+
accounts
|
41
|
+
end
|
42
|
+
|
43
|
+
def fetch_transactions
|
44
|
+
ensure_authenticated
|
45
|
+
|
46
|
+
# https://banking.zionsbank.com/zfnb/userServlet/app/bank/user/register_view_main?actAcct=498282&sortBy=Default&sortOrder=Default
|
47
|
+
|
48
|
+
# The transactions table is messy. Cells we want either have data, curr, datagrey, or currgrey css class
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def ensure_authenticated
|
54
|
+
|
55
|
+
# Check to see if already authenticated
|
56
|
+
page = agent.get('https://banking.zionsbank.com/ibuir')
|
57
|
+
if page.body.include?("SessionTimeOutException") # || (page.links.size > 0 && page.links.first.href == "http://www.zionsbank.com")
|
58
|
+
|
59
|
+
raise ArgumentError, "Username must be supplied before authenticating" unless self.username
|
60
|
+
raise ArgumentError, "Password must be supplied before authenticating" unless self.password
|
61
|
+
|
62
|
+
@agent = Mechanize.new
|
63
|
+
|
64
|
+
# Enter the username
|
65
|
+
page = agent.get('https://zionsbank.com')
|
66
|
+
form = page.form('logonForm')
|
67
|
+
form.publicCred1 = username
|
68
|
+
page = form.submit
|
69
|
+
|
70
|
+
# If the supplied username is incorrect, raise an exception
|
71
|
+
raise "Invalid username" if page.title == "Error Page"
|
72
|
+
|
73
|
+
# Go on to the next page
|
74
|
+
page = page.links.first.click
|
75
|
+
|
76
|
+
# Find the secret question
|
77
|
+
question = page.search('div.form_field')[2].css('div').inner_text
|
78
|
+
|
79
|
+
# If the answer to this question was not supplied, raise an exception
|
80
|
+
raise question unless secret_qas[question]
|
81
|
+
|
82
|
+
# Enter the answer to the secret question
|
83
|
+
form = page.forms.first
|
84
|
+
form["challengeEntry.answerText"] = secret_qas[question]
|
85
|
+
form.radiobutton_with(:value => 'false').check
|
86
|
+
submit_button = form.button_with(:name => '_eventId_submit')
|
87
|
+
page = form.submit(submit_button)
|
88
|
+
|
89
|
+
# If the supplied answer is incorrect, raise an exception
|
90
|
+
raise "Invalid answer" unless page.search('#errorComponent').empty?
|
91
|
+
|
92
|
+
# Enter the password
|
93
|
+
form = page.forms.first
|
94
|
+
form.privateCred1 = password
|
95
|
+
submit_button = form.button_with(:name => '_eventId_submit')
|
96
|
+
page = form.submit(submit_button)
|
97
|
+
|
98
|
+
# If the supplied password is incorrect, raise an exception
|
99
|
+
raise "Invalid password" unless page.search('#errorComponent').empty?
|
100
|
+
|
101
|
+
# Clicking this link logs us into the banking.zionsbank.com domain
|
102
|
+
page = page.links.first.click
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/syrup.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'mechanize'
|
3
|
+
require 'active_support/json'
|
4
|
+
require 'syrup/extract'
|
5
|
+
require 'syrup/account'
|
6
|
+
require 'syrup/transaction'
|
7
|
+
|
8
|
+
# require all institutions
|
9
|
+
require 'syrup/institutions/abstract_institution'
|
10
|
+
Dir[File.dirname(__FILE__) + '/syrup/institutions/*.rb'].each {|file| require file }
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#require 'rubygems'
|
2
|
+
#require 'bundler/setup'
|
3
|
+
|
4
|
+
Bundler.require(:default)
|
5
|
+
|
6
|
+
#RSpec.configure do |config|
|
7
|
+
# config.mock_with :rr
|
8
|
+
# config.before(:each) do
|
9
|
+
#
|
10
|
+
# end
|
11
|
+
#end
|
12
|
+
|
13
|
+
|
14
|
+
# pass in username, password, secret questions
|
15
|
+
|
16
|
+
# zions.fetch_accounts
|
17
|
+
# should I store things?
|
18
|
+
# List accounts
|
19
|
+
# * create an array of Account objects
|
20
|
+
|
21
|
+
# account.transactions OR account.fetch_transactions
|
22
|
+
# zions.
|
23
|
+
# When getting transactions
|
24
|
+
# * create an array of Transaction objects
|
25
|
+
# * populate as many variables on Account as you can (eg. current_balance, etc.)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Syrup::Extract do
|
4
|
+
it "lists all institutions" do
|
5
|
+
Syrup::Extract.list_institutions.size.should == 1
|
6
|
+
puts Syrup::Extract.list_institutions
|
7
|
+
end
|
8
|
+
|
9
|
+
it "creates a Zions Bank institution" do
|
10
|
+
Syrup::Extract.from_institution(:zions_bank).class.should == Syrup::Institutions::ZionsBank
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Syrup::Transaction do
|
4
|
+
before(:all) do
|
5
|
+
@transaction = Syrup::Transaction.new :amount => 10, :payee => "Newegg", :posted_at => DateTime.now
|
6
|
+
end
|
7
|
+
|
8
|
+
it "has an amount" do
|
9
|
+
@transaction.amount.should_not be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "has a payee" do
|
13
|
+
@transaction.payee.should_not be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has a posted-at date" do
|
17
|
+
@transaction.posted_at.should_not be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/syrup.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "syrup/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "syrup"
|
7
|
+
s.version = Syrup::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Don Wilson"]
|
10
|
+
s.email = ["dontangg@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/dontangg/syrup"
|
12
|
+
s.summary = %q{Simple account balance/transactions extractor.}
|
13
|
+
s.description = %q{Simple account balance/transactions extractor by scraping bank websites.}
|
14
|
+
|
15
|
+
s.add_dependency "mechanize"
|
16
|
+
s.add_dependency "activesupport"
|
17
|
+
|
18
|
+
s.add_development_dependency "rspec"
|
19
|
+
|
20
|
+
s.rubyforge_project = s.name
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: syrup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Don Wilson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-02-25 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: mechanize
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "0"
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rspec
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
description: Simple account balance/transactions extractor by scraping bank websites.
|
50
|
+
email:
|
51
|
+
- dontangg@gmail.com
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files: []
|
57
|
+
|
58
|
+
files:
|
59
|
+
- .gitignore
|
60
|
+
- .rspec
|
61
|
+
- CHANGELOG.rdoc
|
62
|
+
- Gemfile
|
63
|
+
- README.rdoc
|
64
|
+
- Rakefile
|
65
|
+
- lib/syrup.rb
|
66
|
+
- lib/syrup/account.rb
|
67
|
+
- lib/syrup/extract.rb
|
68
|
+
- lib/syrup/institutions/abstract_institution.rb
|
69
|
+
- lib/syrup/institutions/zions_bank.rb
|
70
|
+
- lib/syrup/transaction.rb
|
71
|
+
- lib/syrup/version.rb
|
72
|
+
- spec/spec_helper.rb
|
73
|
+
- spec/syrup/extract_spec.rb
|
74
|
+
- spec/syrup/institutions/zions_bank_spec.rb
|
75
|
+
- spec/syrup/transaction_spec.rb
|
76
|
+
- syrup.gemspec
|
77
|
+
has_rdoc: true
|
78
|
+
homepage: http://github.com/dontangg/syrup
|
79
|
+
licenses: []
|
80
|
+
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: "0"
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: "0"
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project: syrup
|
101
|
+
rubygems_version: 1.5.0
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: Simple account balance/transactions extractor.
|
105
|
+
test_files:
|
106
|
+
- spec/spec_helper.rb
|
107
|
+
- spec/syrup/extract_spec.rb
|
108
|
+
- spec/syrup/institutions/zions_bank_spec.rb
|
109
|
+
- spec/syrup/transaction_spec.rb
|