coyodlee 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: ba6d4efb440932238cd8a92bf8f85c0d5e875bd6
4
+ data.tar.gz: 9359ce7258b12ae59b71e42b9168ef3930632940
5
+ SHA512:
6
+ metadata.gz: f1d0518db712d34968cd30e0038aff7d02f6e55f1b070e309fce5ad9c2aa6fd2fd624fa192a17cef982757992e1ea83b56757051987f785450cd7406e9e6ea6f
7
+ data.tar.gz: 8452e4f1ed34d277b49d7d344c0ee7d7c4b58654eab8eb53ddbe70c24f7ea53138f8c1c27b576c5674540166345d7d299cef9b4d493a79a3f680e16932b9a206
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ .env
12
+
13
+ .rbenv-gemsets
14
+ .gems
15
+ *.swp
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.11.2
5
+ env:
6
+ global:
7
+ - secure: uUfBk39flmbyd21ayyqVP4pm1FS7h18joIIAF+547CV6+6Ke7BG3DxR+ivIDG1MVXZNErQ+U5zRAcnG0kWYwbJ1bcFksJYpxR6hN+6MY6SDW7G2oN/Ono6DqHE/tnzrL+/Ax5ui0Yw/W6258tlgm1dj3K28/lmmoK1qbyNqeL5bf+PoggNSIk/DQIHotJsWR4QEIXQM077+PGRfm9TIBE9hU77Cphmu0jPk9NTUHdQHGo6SwDfl2X5CO1Qfp43cn0X7Giir3+tHWZZo8bdDmhDKiTVrLpCFvD6tXHzvU5zhyElUAM4jdPH+jXpmLSK+OwEJispCxHgt6Zd9RELEYMRidlInuxrqYrwikc55shuiIHRq7Sf0JmB2q+sj70+1bNSrJlfPZ6GK3+Ye+HIEGTf8K4B6PUZDrEsPb4fJiaq1kjB5Z5+s3olVqnl0oVIVWk6Pw6qhn0UgVwFMP26eIua7lGo1g9+DqhtZ9WFhBcLDe5f10M7YfaXgDhIW0yXz7Emqip+llpMg//5R2sSQy48M6jjMX4wDcXVrnYFSZAtUno6unxddctdqXQHDpwr60uEyeyb3j5xJp7LUKs7CkOGrfyPqmljD83Ibayh/yBXelme2l6esElWOUVUScuBint5AiPDrnhbApdUGTYbGDPzB4Et3wwfd4ddePTh8z16M=
8
+ - secure: t/ro3jAcnKuJCXqzvbxrukbcwD02+CZIUS6tUXBWexxowbvpC+el60ZLP+q9VD40hBy8SwFf7ukdmtunNRXBUmhPb/NhyBRmtcaHlrvReYV1d8AJX/tGtcuv4OirfcXj45u0mEOtUdDTYYvbbv3cecoKPLtjaZcLct2mwoTh0tcTei0qs/4fZNOm5yGrIVIO5lxyrnGTmWKbSceK9FAwz7FiZ108DrG8QApQNmpXS/5P6G4cnpsilRDXt0p4zMdi/QOgPw7dfANU5K1Hwg7cryBsqk4JwN0mJ6AWTkv4A8THuVAmDzojyqH0Su151HecNu5GOfsXiHzavkvQTdO+s00AkqkyZkzCq8KDx8DxeK363iw/gSVztWv3Q77oDhZ6UyOkPgpzYPbMrl9d2+Z/fC2CrSiVKCLnqjdSbSdhOEFgmc3LTSM7kuGEwtwMGPARmjrC7NVynol4Ji6YWAopO/B79dNNBUSAZRt8yJReN26sIZ3bTqqqIhqzZWvPWWRtYkS7TMu3djqb0ey/0rPI35i7esiIrD26XT+invuehZhRJ+qOikscVjwKSw5wVGoWODOELcL9lF5zccQQDp8xtPvQN8818wekGbBibnrnQ98kzVpR7C1Dl0tEM+ZcdfReC/6a5xL8x1/pS+lGvxN5xqMQFbejc6rKTWJjplUkPY4=
9
+ - secure: emoFvYI3rOkbj8YOOgmLkT7WQtSn/f/D5JTPZ1nl41P8/xaQLGFaa9aSYV3AqsZOER+hDEplek1DFPZNBvgTUND09hpS6qDa9oMGxzJlMxokZIMdHfFBMsb9eXdsvHnG6vilejQ7ic9EfISv4SqwoZmr6KMGBX5o17REtTHwgzuV/aMozhJi5agjWUuLhGFPPTg/fhqLqBZOXCiQjkdhtDkC4PX8FNnwzXWnjn/CuyTZvhapFOftfh76n72FLhMfCpOi3FZnkFcpSvs38uFv7RS9d0ci0XuNFzrhaUbr8WsB+nz1rGKdRUP9RhaU2OJPWmGcGfBeYApG8DhE6dMUJK3k+1X4aPkDjEYm+/xWTjD/2jbRjqvLJJ4B9S75GRz6O3zV4KMc9GkgQWXx+MzCkkc72TsL3XjSCxeqQMvQ8mjl7GFgfeZw5UFiMaCcQzDHECphBAt990rGhwXq0B2eEluOtHo2Kkh7FGvPkCOiMjTdlgNpe2BAh+sf+Bn+/rZ25iFIAWF8IDlfPJc+QiHLtZntwI0afzjWs+2TjDFj3PQ+Shx/Pshk8PXdaPUEP0t7HyLpiWOuyeJaG8O96W/cX51fopmDhlRaVREcpAaJwZ9AXoYSHGXFL9+027L3obG4PYyBF1goBcoe1BZKsLRjGxSftKPmXgCQnBQR3FPYgtM=
10
+ - secure: hrcrel8j12haIsL+8R01+Pc8wNzxB6y2N4jjb7Z1iW8DA6ytZMx+y1e0Ouz+db3k2RxdYOGfT1c0vtJeq6YKfZDuNuTVcOM4nGNfj8Pfe8Ip+faQ8yAuJpqZdq2EnryweUx9MMcCwulfcDiQnYncVeLtm0FByBqE6HjJYPVGFBnzNewAl0JxzpIVLmNXNAxkcBeN/ArKwyAgogUIfL2TRcnY54J8UQSIS2JILRPOkcdkahDQCa02QTaAtHiFtCd4/1P093dbhZIL72nKoGY15m2OWUGf286q9tDydzcZr0vMpoZNugO9I3f4kg4Q9XuIden5U2iLCVl5b9unbCvZsI0s3U4gFCCQhxvZaJRBAvvXzvjPOiBmq0hjwMovqb+nMaLdgLo1JtuZg2b5eZwbIimcGXZVuO+ZonPJxPckk6oyS5qy43IUWv9dMuTbKXiVYLKfoeoOev4c07y9HGyZ4nzkXe1zaBWiPmjjBgcB8zbh8n4jXtcdthlv9LAkHyF53UiUgPlvUll5niPh5bXR5vQBrMIoKzitPKjyUgQCEaugt/tGbsg0zZ1LEeNaeCmuHVfhY7swBMIUQBJ+JNo5ZIkSOcQeDA66oPzJ++zwI2oo30AZNsOhCaZMqTdi1CK0NkDGy8oMtO2qdaqDqWHFgq33e9xrTyQdD6Ihq+hK6HQ=
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at daniel.dyba@pnmac.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'yajl-ruby', require: 'yajl'
4
+
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Daniel Dyba
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,98 @@
1
+ # Coyodlee
2
+ [![Build Status](https://travis-ci.com/pennymac/coyodlee.svg?token=KvBtKQs616ELBMQxp2n7&branch=master)](https://travis-ci.com/pennymac/coyodlee)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/f36b069069540b196fbd/maintainability)](https://codeclimate.com/github/pennymac/coyodlee/maintainability)
4
+
5
+ ## Setup
6
+
7
+ In sandbox mode, point the ```base_url``` to ```https://developer.api.yodlee.com/ysl/restserver/v1```.
8
+
9
+ Export the following environment variables:
10
+
11
+ <table>
12
+ <tr>
13
+ <td><strong>Environment Variables</strong></td>
14
+ <td><strong>Description</td>
15
+ </tr>
16
+ <tr>
17
+ <td>YODLEE_COBRAND_LOGIN</td>
18
+ <td>The Yodlee cobrand login</td>
19
+ <tr/>
20
+ <tr>
21
+ <td>YODLEE_COBRAND_PASSWORD</td>
22
+ <td>The Yodlee cobrand password</td>
23
+ <tr/>
24
+ </table>
25
+
26
+ ## Installation
27
+
28
+ Add this line to your application's Gemfile:
29
+
30
+ ```ruby
31
+ gem 'coyodlee'
32
+ ```
33
+
34
+ And then execute:
35
+
36
+ $ bundle
37
+
38
+ Or install it yourself as:
39
+
40
+ $ gem install coyodlee
41
+
42
+ ## Usage
43
+
44
+ This API exposes a ```setup``` method:
45
+
46
+ ``` ruby
47
+ require 'coyodlee'
48
+
49
+ Coyodlee.setup do |config|
50
+ config.base_url = ENV['YODLEE_BASE_URL']
51
+ config.cobrand_login = ENV['YODLEE_COBRAND_LOGIN']
52
+ config.cobrand_password = ENV['YODLEE_COBRAND_PASSWORD']
53
+ end
54
+
55
+ require 'coyodlee/sessions'
56
+
57
+ cob_session = Coyodlee::Sessions::CobrandSession.new
58
+ cob_session.login login_name: Coyodlee.cobrand_login, password: Coyodlee.cobrand_password
59
+
60
+ user_session = Coyodlee::Sessions::UserSession.new(cobrand_session: cob_session)
61
+ user_session.login login_name: 'yodlee-test-user-login', password: 'yodlee-test-user-password'
62
+
63
+ require 'coyodlee/client'
64
+
65
+ client = Coyodlee::Client.new(user_session)
66
+ resp = client.get_accounts
67
+
68
+ require 'json'
69
+
70
+ # Print all accounts for the test user
71
+ puts JSON.parse(resp.body)
72
+ ```
73
+
74
+ ## Testing
75
+
76
+ To run tests: ```bundle exec rake test```.
77
+
78
+ All tests are written in Minitest and HTTP requests are recorded using VCR.
79
+
80
+ ## Development
81
+
82
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
83
+
84
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
85
+
86
+ ## Why another Yodlee Ruby library?
87
+
88
+ * This library supports Yodlee's newer RESTful API. [yodlee-icious](https://github.com/liftforward/yodlee-icious) supports the older RESTful wrapper over the SOAP API.
89
+ * This library aims to be comprehensive in its feature set. [yodlee](https://github.com/aasmith/yodlee) only supports retrieving data from Yodlee MoneyCenter.
90
+
91
+ ## Contributing
92
+
93
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pennymac/coyodlee. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
94
+
95
+ ## License
96
+
97
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
98
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "coyodlee"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/coyodlee.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'coyodlee/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "coyodlee"
8
+ spec.version = Coyodlee::VERSION
9
+ spec.authors = ["Daniel Dyba"]
10
+ spec.email = ["daniel.dyba@gmail.com"]
11
+
12
+ spec.summary = %q{A Ruby wrapper client for Envestnet's Yodlee API}
13
+ spec.description = %q{A Ruby wrapper client for Envestnet's Yodlee API. For details about the API endpoints, sign in to https://developer.yodlee.com}
14
+ spec.homepage = "https://github.com/pennymac/coyodlee"
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 "rest-client", "~> 2.0"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.11"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest", "~> 5.0"
27
+ spec.add_development_dependency "vcr", "~> 3.0"
28
+ spec.add_development_dependency "webmock", '~> 3.1'
29
+ spec.add_development_dependency "yard", '~> 0.9'
30
+ spec.add_development_dependency "activesupport", '5.1'
31
+ spec.add_development_dependency "travis", '~> 1.8'
32
+ end
@@ -0,0 +1,550 @@
1
+ require_relative 'utils'
2
+
3
+ module Coyodlee
4
+ class Client
5
+ include Utils
6
+
7
+ def initialize(session)
8
+ @session = session
9
+ end
10
+
11
+ def get_accounts
12
+ HttpWrapper.get(
13
+ url: build_url('/accounts'),
14
+ headers: {
15
+ authorization: @session.auth_header,
16
+ accept: :json
17
+ }
18
+ )
19
+ end
20
+
21
+ def get_account_details(account_id:, container:)
22
+ params = [:container]
23
+ .map { |sym| uncapitalized_camelize sym.to_s }
24
+ .zip([container])
25
+ .reject { |query, value| value.to_s.strip.empty? }
26
+ .to_h
27
+ HttpWrapper.get(
28
+ url: build_url("/accounts/#{account_id}"),
29
+ params: params,
30
+ headers: {
31
+ authorization: @session.auth_header,
32
+ accept: :json
33
+ }
34
+ )
35
+ end
36
+
37
+ def update_account(account_id:, body:)
38
+ HttpWrapper.put(
39
+ url: build_url("/accounts/#{account_id}"),
40
+ body: body,
41
+ headers: {
42
+ authorization: @session.auth_header,
43
+ accept: :json,
44
+ content_type: :json
45
+ }
46
+ )
47
+ end
48
+
49
+ def delete_account(id:)
50
+ HttpWrapper.delete(
51
+ url: build_url("/accounts/#{account_id}"),
52
+ headers: {
53
+ authorization: @session.auth_header,
54
+ accept: :json
55
+ }
56
+ )
57
+ end
58
+
59
+ def add_manual_account(body:)
60
+ HttpWrapper.post(
61
+ url: build_url("/accounts"),
62
+ body: body,
63
+ headers: {
64
+ authorization: @session.auth_header,
65
+ accept: :json
66
+ }
67
+ )
68
+ end
69
+
70
+ def get_investment_options(_include: '', account_recon_type: '', account_id: '')
71
+ params = [:_include, :account_recon_type, :account_id]
72
+ .map { |sym| uncapitalized_camelize sym.to_s }
73
+ .map { |sym| sub_underscore sym.to_s }
74
+ .zip([_include, account_recon_type, account_id])
75
+ .reject { |query, value| value.to_s.strip.empty? }
76
+ .to_h
77
+ HttpWrapper.get(
78
+ url: build_url('/accounts/investmentPlan/investmentOptions'),
79
+ params: params,
80
+ headers: {
81
+ authorization: @session.auth_header,
82
+ accept: :json
83
+ }
84
+ )
85
+ end
86
+
87
+ def get_historical_balances(
88
+ include_cf: '',
89
+ account_id: '',
90
+ from_date: '',
91
+ to_date: '',
92
+ interval: '',
93
+ account_recon_type: '',
94
+ skip: '',
95
+ top: ''
96
+ )
97
+ params = [:include_cf, :account_id, :from_date, :to_date, :interval, :account_recon_type, :skip, :top]
98
+ .map { |sym| uncapitalized_camelize sym.to_s }
99
+ .zip([include_cf, account_id, from_date, to_date, interval, account_recon_type, skip, top])
100
+ .reject { |query, value| value.to_s.strip.empty? }
101
+ .to_h
102
+ HttpWrapper.get(
103
+ url: build_url('/accounts/historicalBalances'),
104
+ params: params,
105
+ headers: {
106
+ authorization: @session.auth_header,
107
+ accept: :json
108
+ }
109
+ )
110
+ end
111
+
112
+ def get_holdings(
113
+ _include: '',
114
+ account_id: '',
115
+ provider_account_id: '',
116
+ asset_classification__asset_classification_type: '',
117
+ classification_value: '',
118
+ account_recon_type: ''
119
+ )
120
+ params = [:_include, :account_id, :provider_account_id, :asset_classification__asset_classification_type, :classification_value, :account_recon_type]
121
+ .map { |sym| uncapitalized_camelize sym.to_s }
122
+ .map { |sym| sub_underscore sym.to_s }
123
+ .map { |sym| sub_double_underscore sym.to_s }
124
+ .zip([_include, account_id, provider_account_id, asset_classification__asset_classification_type, classification_value, account_recon_type])
125
+ .reject { |query, value| value.to_s.strip.empty? }
126
+ .to_h
127
+ HttpWrapper.get(
128
+ url: build_url('/holdings'),
129
+ params: params,
130
+ headers: {
131
+ authorization: @session.auth_header,
132
+ accept: :json
133
+ }
134
+ )
135
+ end
136
+
137
+ def get_extended_securities_info(holding_id: '')
138
+ params = [:holding_id]
139
+ .map { |sym| uncapitalized_camelize sym.to_s }
140
+ .zip([holding_id])
141
+ .reject { |query, value| value.to_s.strip.empty? }
142
+ .to_h
143
+ HttpWrapper.get(
144
+ url: build_url('/holdings/securities'),
145
+ params: params,
146
+ headers: {
147
+ authorization: @session.auth_header,
148
+ accept: :json
149
+ }
150
+ )
151
+ end
152
+
153
+ def get_holding_type_list
154
+ HttpWrapper.get(
155
+ url: build_url('/holdings/holdingTypeList'),
156
+ headers: {
157
+ authorization: @session.auth_header,
158
+ accept: :json
159
+ }
160
+ )
161
+ end
162
+
163
+ def get_asset_classification_list
164
+ HttpWrapper.get(
165
+ url: build_url('/holdings/assetClassificationList'),
166
+ headers: {
167
+ authorization: @session.auth_header,
168
+ accept: :json
169
+ }
170
+ )
171
+ end
172
+
173
+ def get_provider_details(provider_id:, provider_account_id:)
174
+ params = [:provider_account_id]
175
+ .map { |sym| uncapitalized_camelize sym.to_s }
176
+ .zip([provider_account_id])
177
+ .reject { |query, value| value.to_s.strip.empty? }
178
+ .to_h
179
+ HttpWrapper.post(
180
+ url: build_url("/providers/#{provider_id}"),
181
+ params: params,
182
+ headers: {
183
+ authorization: @session.auth_header,
184
+ accept: :json
185
+ }
186
+ )
187
+ end
188
+
189
+ def get_providers(
190
+ priority: '',
191
+ capability: '',
192
+ additional_data_set: '',
193
+ name: '',
194
+ skip: '',
195
+ top: '',
196
+ classification: ''
197
+ )
198
+ params = [:priority, :capability, :additional_data_set, :name, :skip, :top, :classification]
199
+ .map { |sym| uncapitalized_camelize sym.to_s }
200
+ .map { |sym| sub_underscore sym.to_s }
201
+ .map { |sym| sub_double_underscore sym.to_s }
202
+ .zip([priority, capability, additional_data_set, name, skip, top, classification])
203
+ .reject { |query, value| value.to_s.strip.empty? }
204
+ .to_h
205
+ HttpWrapper.get(
206
+ url: build_url('/providers'),
207
+ params: params,
208
+ headers: {
209
+ authorization: @session.auth_header,
210
+ accept: :json
211
+ }
212
+ )
213
+ end
214
+
215
+ def verify_provider_account(body:)
216
+ HttpWrapper.put(
217
+ url: build_url('/providerAccounts/verification'),
218
+ body: body,
219
+ headers: {
220
+ authorization: @session.auth_header,
221
+ accept: :json
222
+ }
223
+ )
224
+ end
225
+
226
+ def get_verification_status(provider_account_id:)
227
+ HttpWrapper.get(
228
+ url: build_url("/providerAccounts/verification/#{provider_account_id}"),
229
+ body: body,
230
+ headers: {
231
+ authorization: @session.auth_header,
232
+ accept: :json
233
+ }
234
+ )
235
+ end
236
+
237
+ def update_provider_account(body:)
238
+ HttpWrapper.put(
239
+ url: build_url('/provideAccounts'),
240
+ body: body,
241
+ headers: {
242
+ authorization: @session.auth_header,
243
+ accept: :json
244
+ }
245
+ )
246
+ end
247
+
248
+ def delete_provider_account(provider_account_id:)
249
+ HttpWrapper.delete(
250
+ url: build_url("/providerAccounts/#{provider_account_id}"),
251
+ headers: {
252
+ authorization: @session.auth_header,
253
+ accept: :json
254
+ }
255
+ )
256
+ end
257
+
258
+ def get_provider_account_details(provider_account_id:, _include: '')
259
+ params = [:provider_account_id, :_include]
260
+ .map { |sym| uncapitalized_camelize sym.to_s }
261
+ .zip([provider_account_id, _include])
262
+ .reject { |query, value| value.to_s.strip.empty? }
263
+ .to_h
264
+ HttpWrapper.get(
265
+ url: build_url("/providerAccounts/#{provider_account_id}"),
266
+ params: params,
267
+ headers: {
268
+ authorization: @session.auth_header,
269
+ accept: :json
270
+ }
271
+ )
272
+ end
273
+
274
+ def get_provider_accounts
275
+ HttpWrapper.get(
276
+ url: build_url('/providerAccounts'),
277
+ headers: {
278
+ authorization: @session.auth_header,
279
+ accept: :json
280
+ }
281
+ )
282
+ end
283
+
284
+ def add_provider_account(provider_id:, body:)
285
+ params = [:provider_id]
286
+ .map { |sym| uncapitalized_camelize sym.to_s }
287
+ .zip([provider_id])
288
+ .reject { |query, value| value.to_s.strip.empty? }
289
+ .to_h
290
+ HttpWrapper.post(
291
+ url: build_url('/providerAccounts'),
292
+ params: params,
293
+ body: body,
294
+ headers: {
295
+ authorization: @session.auth_header,
296
+ accept: :json
297
+ }
298
+ )
299
+ end
300
+
301
+ def get_transactions_count(params={})
302
+ params = params
303
+ .map { |sym, val| [uncapitalized_camelize(sym.to_s), val] }
304
+ .reject { |_, value| value.to_s.strip.empty? }
305
+ .to_h
306
+ HttpWrapper.get(
307
+ url: build_url('/transactions/count'),
308
+ params: params,
309
+ headers: {
310
+ authorization: @session.auth_header,
311
+ accept: :json
312
+ }
313
+ )
314
+ end
315
+
316
+ def get_transaction_categorization_rules
317
+ HttpWrapper.get(
318
+ url: build_url('/transactions/categories/rules'),
319
+ headers: {
320
+ authorization: @session.auth_header,
321
+ accept: :json
322
+ }
323
+ )
324
+ end
325
+
326
+ def create_transaction_categorization_rule(body:)
327
+ HttpWrapper.post(
328
+ url: build_url('/transactions/categories/rules'),
329
+ body: body,
330
+ headers: {
331
+ authorization: @session.auth_header,
332
+ accept: :json
333
+ }
334
+ )
335
+ end
336
+
337
+ def update_transaction_categorization_rule(rule_id:, body:)
338
+ HttpWrapper.put(
339
+ url: build_url("/transactions/categories/rules/#{rule_id}"),
340
+ body: body,
341
+ headers: {
342
+ authorization: @session.auth_header,
343
+ accept: :json
344
+ }
345
+ )
346
+ end
347
+
348
+ def delete_transaction_categorization_rule(rule_id:)
349
+ HttpWrapper.delete(
350
+ url: build_url("/transactions/categories/rules/#{rule_id}"),
351
+ headers: {
352
+ authorization: @session.auth_header,
353
+ accept: :json
354
+ }
355
+ )
356
+ end
357
+
358
+ def run_transaction_categorization_rule(rule_id:)
359
+ params = { action: 'run'}
360
+ HttpWrapper.post(
361
+ url: build_url("/transactions/categories/rules/#{rule_id}"),
362
+ params: params,
363
+ headers: {
364
+ authorization: @session.auth_header,
365
+ accept: :json
366
+ }
367
+ )
368
+ end
369
+
370
+ def run_all_transaction_categorization_rules
371
+ params = { action: 'run'}
372
+ HttpWrapper.post(
373
+ url: build_url('/transactions/categories/rules'),
374
+ params: params,
375
+ headers: {
376
+ authorization: @session.auth_header,
377
+ accept: :json
378
+ }
379
+ )
380
+ end
381
+
382
+ def delete_transaction_category(category_id:)
383
+ HttpWrapper.delete(
384
+ url: build_url("/transactions/categories/#{category_id}"),
385
+ headers: {
386
+ authorization: @session.auth_header,
387
+ accept: :json
388
+ }
389
+ )
390
+ end
391
+
392
+ def update_transaction_category(category_id:, container:, category_name:, transaction_id:)
393
+ params = [:container, :category_name, :transaction_id]
394
+ .map { |sym, val| [uncapitalized_camelize(sym.to_s), val] }
395
+ .zip([container, category_name, transaction_id])
396
+ .reject { |_, value| value.to_s.strip.empty? }
397
+ .to_h
398
+ HttpWrapper.put(
399
+ url: build_url("/transactions/categories/#{category_id}"),
400
+ params: params,
401
+ headers: {
402
+ authorization: @session.auth_header,
403
+ accept: :json
404
+ }
405
+ )
406
+ end
407
+
408
+ def get_transaction_category_list
409
+ HttpWrapper.get(
410
+ url: build_url('/transactions/categories'),
411
+ headers: {
412
+ authorization: @session.auth_header,
413
+ accept: :json
414
+ }
415
+ )
416
+ end
417
+
418
+ def create_category(body:)
419
+ HttpWrapper.post(
420
+ url: build_url('/transactions/categories'),
421
+ body: body,
422
+ headers: {
423
+ authorization: @session.auth_header,
424
+ accept: :json
425
+ }
426
+ )
427
+ end
428
+
429
+ def update_category(body:)
430
+ HttpWrapper.put(
431
+ url: build_url('/transactions/categories'),
432
+ body: body,
433
+ headers: {
434
+ authorization: @session.auth_header,
435
+ accept: :json
436
+ }
437
+ )
438
+ end
439
+
440
+ def get_transactions(params={})
441
+ params = params
442
+ .map { |sym, val| [uncapitalized_camelize(sym.to_s), val] }
443
+ .reject { |_, value| value.to_s.strip.empty? }
444
+ .to_h
445
+ HttpWrapper.get(
446
+ url: build_url('/transactions'),
447
+ params: params,
448
+ headers: {
449
+ authorization: @session.auth_header,
450
+ accept: :json
451
+ }
452
+ )
453
+ end
454
+
455
+ def get_statements(params={})
456
+ params = params
457
+ .map { |sym, val| [uncapitalized_camelize(sym.to_s), val] }
458
+ .reject { |_, value| value.to_s.strip.empty? }
459
+ .to_h
460
+ HttpWrapper.get(
461
+ url: build_url('/statements'),
462
+ params: params,
463
+ headers: {
464
+ authorization: @session.auth_header,
465
+ accept: :json
466
+ }
467
+ )
468
+ end
469
+
470
+ def get_transaction_summary(group_by:, params: {})
471
+ params = params
472
+ .merge({ group_by: group_by })
473
+ .map { |sym, val| [uncapitalized_camelize(sym.to_s), val] }
474
+ .reject { |_, value| value.to_s.strip.empty? }
475
+ .to_h
476
+ HttpWrapper.get(
477
+ url: build_url('/derived/transactionSummary'),
478
+ params: params,
479
+ headers: {
480
+ authorization: @session.auth_header,
481
+ accept: :json
482
+ }
483
+ )
484
+ end
485
+
486
+ def get_holding_summary(params={})
487
+ params = params
488
+ .map { |sym, val| [uncapitalized_camelize(sym.to_s), val] }
489
+ .map { |sym, val| [sub_underscore(sym.to_s), val] }
490
+ .reject { |_, value| value.to_s.strip.empty? }
491
+ .to_h
492
+ HttpWrapper.get(
493
+ url: build_url('/derived/holdingSummary'),
494
+ params: params,
495
+ headers: {
496
+ authorization: @session.auth_header,
497
+ accept: :json
498
+ }
499
+ )
500
+ end
501
+
502
+ def get_networth_summary(params={})
503
+ params = params
504
+ .map { |sym, val| [uncapitalized_camelize(sym.to_s), val] }
505
+ .map { |sym, val| [sub_underscore(sym.to_s), val] }
506
+ .reject { |_, value| value.to_s.strip.empty? }
507
+ .to_h
508
+ HttpWrapper.get(
509
+ url: build_url('/derived/networth'),
510
+ params: params,
511
+ headers: {
512
+ authorization: @session.auth_header,
513
+ accept: :json
514
+ }
515
+ )
516
+ end
517
+
518
+ def get_extract_events(event_name:, from_date:, to_date:)
519
+ params = [:event_name, :from_date, :to_date]
520
+ .map { |sym| uncapitalized_camelize sym.to_s }
521
+ .zip([event_name, from_date, to_date])
522
+ .reject { |_, value| value.to_s.strip.empty? }
523
+ .to_h
524
+ HttpWrapper.get(
525
+ url: build_url('/dataExtracts/events'),
526
+ params: params,
527
+ headers: {
528
+ authorization: @session.auth_header,
529
+ accept: :json
530
+ }
531
+ )
532
+ end
533
+
534
+ def get_user_data(login_name:, from_date:, to_date:)
535
+ params = [:login_name, :from_date, :to_date]
536
+ .map { |sym| uncapitalized_camelize sym.to_s }
537
+ .zip([login_name, from_date, to_date])
538
+ .reject { |_, value| value.to_s.strip.empty? }
539
+ .to_h
540
+ HttpWrapper.get(
541
+ url: build_url('/dataExtracts/userData'),
542
+ params: params,
543
+ headers: {
544
+ authorization: @session.auth_header,
545
+ accept: :json
546
+ }
547
+ )
548
+ end
549
+ end
550
+ end
@@ -0,0 +1,27 @@
1
+ require "rest-client"
2
+
3
+ module Coyodlee
4
+ module HttpWrapper
5
+ class << self
6
+ def get(url:, params: {}, headers: {})
7
+ if params.empty?
8
+ RestClient.get(url, headers)
9
+ else
10
+ RestClient.get(url, { params: params }.merge(headers))
11
+ end
12
+ end
13
+
14
+ def post(url:, body:, headers: {})
15
+ RestClient.post(url, body, headers)
16
+ end
17
+
18
+ def put(url:, body:, headers: {})
19
+ RestClient.put(url, body, headers)
20
+ end
21
+
22
+ def delete(url:, headers: {})
23
+ RestClient.delete(url, nil, headers)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,50 @@
1
+ require 'json'
2
+
3
+ module Coyodlee
4
+ module Sessions
5
+ class CobrandSession
6
+ # Holds the cobrand session token
7
+ #
8
+ # @return [String] the cobrand session token
9
+ attr_reader :token
10
+
11
+ # Creates a new cobrand session
12
+ def initialize
13
+ @token = ''
14
+ end
15
+
16
+ # Initiates a cobrand session
17
+ #
18
+ # @return the underlying response object for the HTTP client you've selected, RestClient by default
19
+ def login(login_name:, password:)
20
+ HttpWrapper.post(
21
+ url: "#{::Coyodlee.base_url}/cobrand/login",
22
+ body: {
23
+ cobrand: {
24
+ cobrandLogin: login_name,
25
+ cobrandPassword: password,
26
+ locale: 'en_US'
27
+ }
28
+ }.to_json,
29
+ headers: { content_type: :json, accept: :json }
30
+ ).tap { |response|
31
+ @token = JSON.parse(response.body)['session']['cobSession']
32
+ }
33
+ end
34
+
35
+ # Returns a string containing the cobrand session token which can be used as the value of the Authorization HTTP header
36
+ def auth_header
37
+ @token.empty? ? '' : "cobSession=#{@token}"
38
+ end
39
+
40
+ # Terminates the cobrand session
41
+ #
42
+ # @return the underlying response object for the HTTP client you've selected, RestClient by default
43
+ def logout
44
+ HttpWrapper.post(
45
+ url: "#{::Coyodlee.base_url}/cobrand/logout"
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,60 @@
1
+ require 'json'
2
+
3
+ module Coyodlee
4
+ module Sessions
5
+ class UserSession
6
+ # Holds the user session token
7
+ #
8
+ # @return [String] the user session token
9
+ attr_reader :token
10
+
11
+ # Creates a new user session
12
+ #
13
+ # @param cobrand_session [CobrandSession] the cobrand session
14
+ def initialize(cobrand_session:)
15
+ @token = ''
16
+ @cobrand_session = cobrand_session
17
+ end
18
+
19
+ # Initiates a user session. If the login is successful, the {#token} will be
20
+ # set
21
+ #
22
+ # @param login_name [String] the login name of the user
23
+ # @param password [String] the password of the user
24
+ # @return the underlying response object for the HTTP client you've selected, RestClient by default
25
+ def login(login_name:, password:)
26
+ HttpWrapper.post(
27
+ url: "#{::Coyodlee.base_url}/user/login",
28
+ body: {
29
+ user: {
30
+ loginName: login_name,
31
+ password: password,
32
+ locale: 'en_US'
33
+ }
34
+ }.to_json,
35
+ headers: {
36
+ content_type: :json,
37
+ accept: :json,
38
+ authorization: @cobrand_session.auth_header
39
+ }
40
+ ).tap { |response|
41
+ @token = JSON.parse(response.body)['user']['session']['userSession']
42
+ }
43
+ end
44
+
45
+ # Returns a string containing the cobrand session token and the user session token which can be used as the value of the Authorization HTTP header
46
+ def auth_header
47
+ @token.empty? ? '' : "cobSession=#{@cobrand_session.token},userSession=#{@token}"
48
+ end
49
+
50
+ # Terminates the user session
51
+ #
52
+ # @return the underlying response object for the HTTP client you've selected, RestClient by default
53
+ def logout
54
+ HttpWrapper.post(
55
+ url: "#{::Coyodlee.base_url}/user/logout"
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'sessions/cobrand_session'
2
+ require_relative 'sessions/user_session'
@@ -0,0 +1,26 @@
1
+ module Coyodlee
2
+ module Utils
3
+ # Converts a string to camel-case with the first letter uncapitalized
4
+ # @param str [String] The string to modify
5
+ # @return [String] The string as camel-cased with the first letter uncapitalized
6
+ def uncapitalized_camelize(str)
7
+ str
8
+ .split('_')
9
+ .map { |w| w.capitalize }
10
+ .join
11
+ .tap { |w| w[0] = w[0].downcase }
12
+ end
13
+
14
+ def sub_underscore(str)
15
+ str.sub(/^_/, '')
16
+ end
17
+
18
+ def sub_double_underscore(str)
19
+ str.gsub(/__/, '.')
20
+ end
21
+
22
+ def build_url(path)
23
+ ::Coyodlee.base_url.to_s + path
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module Coyodlee
2
+ VERSION = "0.1.0"
3
+ end
data/lib/coyodlee.rb ADDED
@@ -0,0 +1,26 @@
1
+ require_relative 'coyodlee/version'
2
+ require_relative 'coyodlee/http_wrapper'
3
+ require 'json'
4
+ require 'yajl'
5
+
6
+ # The global Yodlee configuration object
7
+ module Coyodlee
8
+
9
+ class << self
10
+ # The base url Yodlee provides for your cobrand
11
+ # @return [String] The base url Yodlee provides for your cobrand
12
+ attr_accessor :base_url
13
+ # The login of your cobrand
14
+ # @return [String] The login of your cobrand
15
+ attr_accessor :cobrand_login
16
+ # The password of your cobrand
17
+ # @return [String] The password of your cobrand
18
+ attr_accessor :cobrand_password
19
+
20
+ # The method to configure Yodlee parameters. Use this to set the global parameters such as {Yodlee.base_url}, {Yodlee.cobrand_login}, and {Yodlee.cobrand_password}
21
+ # @yieldparam config [Yodlee] The Yodlee object
22
+ def setup &block
23
+ yield self
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,189 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coyodlee
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Dyba
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: vcr
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: activesupport
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '='
116
+ - !ruby/object:Gem::Version
117
+ version: '5.1'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '='
123
+ - !ruby/object:Gem::Version
124
+ version: '5.1'
125
+ - !ruby/object:Gem::Dependency
126
+ name: travis
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.8'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.8'
139
+ description: A Ruby wrapper client for Envestnet's Yodlee API. For details about the
140
+ API endpoints, sign in to https://developer.yodlee.com
141
+ email:
142
+ - daniel.dyba@gmail.com
143
+ executables: []
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - ".gitignore"
148
+ - ".travis.yml"
149
+ - CODE_OF_CONDUCT.md
150
+ - Gemfile
151
+ - LICENSE.txt
152
+ - README.md
153
+ - Rakefile
154
+ - bin/console
155
+ - bin/setup
156
+ - coyodlee.gemspec
157
+ - lib/coyodlee.rb
158
+ - lib/coyodlee/client.rb
159
+ - lib/coyodlee/http_wrapper.rb
160
+ - lib/coyodlee/sessions.rb
161
+ - lib/coyodlee/sessions/cobrand_session.rb
162
+ - lib/coyodlee/sessions/user_session.rb
163
+ - lib/coyodlee/utils.rb
164
+ - lib/coyodlee/version.rb
165
+ homepage: https://github.com/pennymac/coyodlee
166
+ licenses:
167
+ - MIT
168
+ metadata: {}
169
+ post_install_message:
170
+ rdoc_options: []
171
+ require_paths:
172
+ - lib
173
+ required_ruby_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ required_rubygems_version: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ requirements: []
184
+ rubyforge_project:
185
+ rubygems_version: 2.5.2
186
+ signing_key:
187
+ specification_version: 4
188
+ summary: A Ruby wrapper client for Envestnet's Yodlee API
189
+ test_files: []