ruby_mint 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 42561a5237e90022049c08135df7a1af5e9ee9f7
4
+ data.tar.gz: cebbafc6bb874c9d63487982e70adc83ca6c6f4e
5
+ SHA512:
6
+ metadata.gz: c46009068385a016cc3ed4cbb42c4286272f234850c8ab95111fdb00d253d5a7b4b9c771aa7c2d0f75f3d3b775d6b6fbfe38b00316fdda843c78dde3ffbbd005
7
+ data.tar.gz: d96c4583c9d647d2162974c96642a9bbcd62f563ef89894f733c925ec2631a70c32a91e720a27de95e4103220f3ba89256882fdaf47a9b79beb75694b537b53e
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.3
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruby_mint.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Nick Marrone
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # RubyMint
2
+
3
+ RubyMint is a gem to assist you with getting information from Mint.com's API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ruby_mint'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install ruby_mint
20
+
21
+ ## Usage
22
+
23
+ # Initiate the Gem
24
+
25
+ RubyMint must be initiated using an email address and password. Prior to making
26
+ any calls to the API, you must also login.
27
+
28
+ ```ruby
29
+ ruby_mint = RubyMint.new('myemail@foobar.com', 'mysecretpassword')
30
+ ruby_mint.login
31
+ ```
32
+
33
+ # Refreshing Data
34
+
35
+ Mint.com does not keep financial data 100% up to date, so if you want to get the most recent
36
+ account and transaction data you must first refresh the data. Once a refresh has been
37
+ initiated, it takes at least a few seconds for Mint.com to complete. There is also a method
38
+ to check refresh status.
39
+
40
+ ```ruby
41
+ ruby_mint.initiate_account_refresh
42
+ ruby_mint.refreshing?
43
+ ```
44
+
45
+ For convenience, you can also include a block to execute once refreshing is complete. There is
46
+ an optional parameter to indicate how many seconds to wait between calls to check if the Mint.com
47
+ is still refreshing.
48
+
49
+ ```ruby
50
+ ruby_mint.initiate_account_refresh(3) do
51
+ # Get your mint data once refresh is complete
52
+ puts "Refresh complete!"
53
+ end
54
+ ```
55
+
56
+ # Account Data
57
+
58
+ Account data is returned as a hash.
59
+
60
+ ```ruby
61
+ accounts = ruby_mint.accounts
62
+ ```
63
+
64
+ # Transaction Data
65
+
66
+ Mint.com returns transactions as CSV.
67
+
68
+ ```ruby
69
+ transactions = ruby_mint.transactions
70
+ ```
71
+
72
+ ## Contributing
73
+
74
+ 1. Fork it ( https://github.com/nickmarrone/ruby_mint/fork )
75
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
76
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
77
+ 4. Push to the branch (`git push origin my-new-feature`)
78
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,100 @@
1
+ require 'ruby_mint'
2
+ require 'csv'
3
+ require 'io/console'
4
+
5
+ def print_usage
6
+ puts <<-USAGE
7
+ Usage: ruby #{__FILE__} <-a=account_output> <-t=transaction_output>
8
+ * -a=account_output: CSV file to append account data to (default to mint_accounts.csv)
9
+ * -t=transaction_output: CSV file to write transaction data to (default to mint_transactions.csv)
10
+ USAGE
11
+ exit
12
+ end
13
+
14
+ def fail!(error_msg)
15
+ puts "Error: #{error_msg}"
16
+ print_usage
17
+ end
18
+
19
+ accounts_file = nil
20
+ transactions_file = nil
21
+
22
+ if ARGV.length == 0
23
+ accounts_file = "mint_accounts.csv"
24
+ end
25
+
26
+ ARGV.each do |arg|
27
+ command, filename = arg.split('=')
28
+ case command
29
+ when '-a'
30
+ accounts_file = filename
31
+ when '-t'
32
+ transactions_file = filename
33
+ else
34
+ print_usage
35
+ exit
36
+ end
37
+ end
38
+
39
+ # Get username and password
40
+ print "Email: "
41
+ email = STDIN.gets.chomp
42
+
43
+ print "Password: "
44
+ password = STDIN.noecho(&:gets).chomp
45
+ print "\n"
46
+
47
+ ruby_mint = RubyMint.new(email, password)
48
+
49
+ puts "Logging in..."
50
+ ruby_mint.login
51
+
52
+ accounts = nil
53
+ transactions = nil
54
+ puts "Refreshing account..."
55
+
56
+ ruby_mint.initiate_account_refresh do
57
+ if accounts_file
58
+ puts "Downloading accounts..."
59
+ accounts = ruby_mint.accounts
60
+ end
61
+
62
+ if transactions_file
63
+ puts "Downloading transactions..."
64
+ transactions = ruby_mint.transactions
65
+ end
66
+ end
67
+
68
+ puts "Writing data..."
69
+ timestamp = Time.now.strftime("%Y-%m-%d-%H%M")
70
+
71
+ if accounts_file
72
+ # Check if file already exists
73
+ file_exists = File.file?(accounts_file)
74
+
75
+ CSV.open(accounts_file, "a") do |csv|
76
+ if !file_exists
77
+ csv << ['id', 'timestamp', 'name', 'subName', 'class', 'value', 'currentBalance']
78
+ end
79
+
80
+ accounts.each do |account|
81
+ csv << [
82
+ account["id"],
83
+ timestamp,
84
+ account["fiName"],
85
+ account["accountName"],
86
+ account["klass"],
87
+ account["value"],
88
+ account["currentBalance"]]
89
+ end
90
+ end
91
+ end
92
+
93
+ if transactions_file
94
+ # Write transactions to file
95
+ File.open(transactions_file, "w") do |output|
96
+ output.puts transactions
97
+ end
98
+ end
99
+
100
+ puts "Complete!"
@@ -0,0 +1,3 @@
1
+ class RubyMint
2
+ VERSION = "0.1.0"
3
+ end
data/lib/ruby_mint.rb ADDED
@@ -0,0 +1,143 @@
1
+ require "ruby_mint/version"
2
+ require "mechanize"
3
+ require "json"
4
+
5
+ class RubyMintError < StandardError; end
6
+
7
+ class RubyMint
8
+ JSON_HEADERS = { "accept" => "application/json" }
9
+
10
+ ACCOUNT_TYPES = [
11
+ "BANK",
12
+ "CREDIT",
13
+ "INVESTMENT",
14
+ "LOAN",
15
+ "MORTGAGE",
16
+ "OTHER_PROPERTY",
17
+ "REAL_ESTATE",
18
+ "VEHICLE",
19
+ "UNCLASSIFIED",
20
+ ]
21
+
22
+ # Initialize RubyMint
23
+ #
24
+ # @param username [String] usually an email address
25
+ # @param password [String]
26
+ def initialize(username, password)
27
+ @username = username
28
+ @password = password
29
+ @request_id = 34
30
+
31
+ @token = nil
32
+ end
33
+
34
+ # Login retrieves a user token from Mint.com
35
+ def login
36
+ response = agent.get("https://wwws.mint.com/login.event?task=L")
37
+ raise RubyMintError.new("Unable to GET Mint login page.") if response.code != "200"
38
+
39
+ response = agent.post("https://wwws.mint.com/getUserPod.xevent", { "username" => @username }, JSON_HEADERS)
40
+ raise RubyMintError.new("Unable to POST to getUserPod.") if response.code != "200"
41
+
42
+ query = {
43
+ "username" => @username,
44
+ "password" => @password,
45
+ "task" => "L",
46
+ "browser" => "firefox",
47
+ "browserVersion" => "27",
48
+ "os" => "Linux",
49
+ }
50
+
51
+ response = agent.post("https://wwws.mint.com/loginUserSubmit.xevent", query, JSON_HEADERS)
52
+ if response.code != "200" || !response.body.include?("token")
53
+ raise RubyMintError.new("Mint.com login failed. Response code: #{response.code}")
54
+ end
55
+
56
+ login_body = JSON.load(response.body)
57
+ if !login_body || !login_body["sUser"] || !login_body["sUser"]["token"]
58
+ raise RubyMintError.new("Mint.com login failed (no token in login response body).")
59
+ end
60
+
61
+ @token = login_body["sUser"]["token"]
62
+ end
63
+
64
+ # Check if user is logged in already by the presence of a token.
65
+ #
66
+ # @returns [Boolean]
67
+ def logged_in?
68
+ !@token.nil?
69
+ end
70
+
71
+ # Request that Mint.com refresh its account and transaction data
72
+ #
73
+ # @param sleep_time [Integer] Num of seconds to wait between calls to refreshing? when block is passed
74
+ # @param block [Block] Code to execute upon completion of of refreshing
75
+ def initiate_account_refresh(sleep_time = 3, &block)
76
+ agent.post("https://wwws.mint.com/refreshFILogins.xevent", { "token" => @token }, JSON_HEADERS)
77
+ if block
78
+ loop{ sleep sleep_time; break if !refreshing? }
79
+ yield
80
+ end
81
+ end
82
+
83
+ # Is Mint.com in the process of refreshing its data?
84
+ #
85
+ # @returns [Boolean]
86
+ def refreshing?
87
+ response = agent.get("https://wwws.mint.com/userStatus.xevent", JSON_HEADERS)
88
+ if response.code != "200" || !response.body.include?("isRefreshing")
89
+ raise RubyMintError.new("Unable to check if account is refreshing.")
90
+ end
91
+
92
+ JSON.parse(response.body)["isRefreshing"]
93
+ end
94
+
95
+ # Get account data
96
+ #
97
+ # @param account_types [Array<String>] Type of accounts to retrieve. Defaults to all types.
98
+ # @returns [Hash]
99
+ def accounts(account_types = ACCOUNT_TYPES)
100
+ # Use a new request_id
101
+ @request_id += 1
102
+
103
+ account_query = {
104
+ "input" => JSON.dump([{
105
+ "args" => { "types" => account_types },
106
+ "id" => @request_id.to_s,
107
+ "service" => "MintAccountService",
108
+ "task" => "getAccountsSorted",
109
+ }])}
110
+
111
+ # Use token to get list of accounts
112
+ results = agent.post("https://wwws.mint.com/bundledServiceController.xevent?legacy=false&token=#{@token}", account_query, JSON_HEADERS)
113
+ raise RubyMintError.new("Unable to obtain account information. Response code: #{results.code}") if results.code != "200"
114
+
115
+ account_body = JSON.load(results.body)
116
+ if !account_body || !account_body["response"] || !account_body["response"][@request_id.to_s] || !account_body["response"][@request_id.to_s]["response"]
117
+ raise RubyMintError.new("Unable to obtain account information (no account information in response).")
118
+ end
119
+
120
+ account_body["response"][@request_id.to_s]["response"]
121
+ end
122
+
123
+ # Get transactions from mint. They are returned as CSV and include ALL
124
+ # the transactions available
125
+ #
126
+ # @returns [String] CSV of all transactions
127
+ def transactions
128
+ results = agent.get("https://wwws.mint.com/transactionDownload.event", JSON_HEADERS)
129
+ raise RubyMintError.new("Unable to obtain transations.") if results.code != "200"
130
+ raise RubyMintError.new("Non-CSV content returned.") if !results.header["content-type"].include?("text/csv")
131
+
132
+ results.body
133
+ end
134
+
135
+
136
+ private
137
+
138
+ def agent
139
+ @agent ||= Mechanize.new { |agent|
140
+ agent.user_agent_alias = 'Linux Firefox'
141
+ }
142
+ end
143
+ end
data/ruby_mint.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ruby_mint/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ruby_mint"
8
+ spec.version = RubyMint::VERSION
9
+ spec.authors = ["Nick Marrone"]
10
+ spec.email = ["nickmarrone@gmail.com"]
11
+
12
+ spec.summary = "Retrieve your Mint.com transaction and account information."
13
+ spec.description = "Gem to allow you to incorporate Mint.com into your ruby applications."
14
+ spec.homepage = "https://github.com/nickmarrone/ruby_mint"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "mechanize", "~> 2.7"
23
+ spec.add_dependency "json", "~> 1.8"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.8"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "pry", "~> 0.10"
28
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_mint
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nick Marrone
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-03-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mechanize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.8'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.10'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.10'
83
+ description: Gem to allow you to incorporate Mint.com into your ruby applications.
84
+ email:
85
+ - nickmarrone@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".travis.yml"
92
+ - CODE_OF_CONDUCT.md
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/mint_download.rb
98
+ - lib/ruby_mint.rb
99
+ - lib/ruby_mint/version.rb
100
+ - ruby_mint.gemspec
101
+ homepage: https://github.com/nickmarrone/ruby_mint
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.2.2
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Retrieve your Mint.com transaction and account information.
125
+ test_files: []