starling-ruby 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7792e100b4e4f2815be54969b6f93eb7dc462cea
4
+ data.tar.gz: 39494875175595c11bd964e26d3790332e863dca
5
+ SHA512:
6
+ metadata.gz: e82c0ca11d00d1665fdbc5feea79817dfb77065796905a05ffc15a33985db6e7621bdf4763133e7b484f92d565905195b5d48b5ef6dde7d823223861655a9f50
7
+ data.tar.gz: 612492a35d4de1bb25e8d9d8a01abd3bca0edecc5f04a29a33bda3ab7417c12b5dc8e48fc0005a723c984e289fe03c4406df5f3385a0959f88983fd80293bc52
@@ -0,0 +1,39 @@
1
+ version: 2
2
+ jobs:
3
+ build:
4
+ working_directory: /home/circleci/starling
5
+ docker:
6
+ - image: circleci/ruby:2.4.1-node
7
+ steps:
8
+ - checkout
9
+
10
+ # Restore bundle cache
11
+ - type: cache-restore
12
+ key: starling-{{ checksum "Gemfile.lock" }}
13
+
14
+ # Bundle install dependencies
15
+ - run: bundle install --path vendor/bundle
16
+
17
+ # Store bundle cache
18
+ - type: cache-save
19
+ key: starling-{{ checksum "Gemfile.lock" }}
20
+ paths:
21
+ - vendor/bundle
22
+
23
+ # Run RSpec in parallel
24
+ - type: shell
25
+ command: |
26
+ bundle exec rspec --profile 10 \
27
+ --format RspecJunitFormatter \
28
+ --out /tmp/test-results/rspec.xml \
29
+ --format progress \
30
+ $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
31
+
32
+ # Run Rubocop
33
+ - type: shell
34
+ command: |
35
+ bundle exec rubocop
36
+
37
+ # Save artifacts
38
+ - type: store_test_results
39
+ path: /tmp/test-results
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /vendor
10
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,12 @@
1
+ AllCops:
2
+ DisplayCopNames: true
3
+
4
+ Metrics/LineLength:
5
+ Max: 90
6
+
7
+ Style/Documentation:
8
+ Enabled: false
9
+
10
+ Metrics/BlockLength:
11
+ Exclude:
12
+ - spec/**/*
data/CHANGELOG.md ADDED
File without changes
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at tim@gocardless.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in starling.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,87 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ starling-ruby (0.1.0)
5
+ faraday (>= 0.8.9, < 0.10)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.5.1)
11
+ public_suffix (~> 2.0, >= 2.0.2)
12
+ ast (2.3.0)
13
+ builder (3.2.3)
14
+ coderay (1.1.1)
15
+ crack (0.4.3)
16
+ safe_yaml (~> 1.0.0)
17
+ diff-lcs (1.3)
18
+ faraday (0.9.2)
19
+ multipart-post (>= 1.2, < 3)
20
+ hashdiff (0.3.4)
21
+ method_source (0.8.2)
22
+ multipart-post (2.0.0)
23
+ parallel (1.11.2)
24
+ parser (2.4.0.0)
25
+ ast (~> 2.2)
26
+ powerpack (0.1.1)
27
+ pry (0.10.4)
28
+ coderay (~> 1.1.0)
29
+ method_source (~> 0.8.1)
30
+ slop (~> 3.4)
31
+ public_suffix (2.0.5)
32
+ rainbow (2.2.2)
33
+ rake
34
+ rake (10.5.0)
35
+ rspec (3.6.0)
36
+ rspec-core (~> 3.6.0)
37
+ rspec-expectations (~> 3.6.0)
38
+ rspec-mocks (~> 3.6.0)
39
+ rspec-core (3.6.0)
40
+ rspec-support (~> 3.6.0)
41
+ rspec-expectations (3.6.0)
42
+ diff-lcs (>= 1.2.0, < 2.0)
43
+ rspec-support (~> 3.6.0)
44
+ rspec-its (1.2.0)
45
+ rspec-core (>= 3.0.0)
46
+ rspec-expectations (>= 3.0.0)
47
+ rspec-mocks (3.6.0)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.6.0)
50
+ rspec-support (3.6.0)
51
+ rspec_junit_formatter (0.2.3)
52
+ builder (< 4)
53
+ rspec-core (>= 2, < 4, != 2.12.0)
54
+ rubocop (0.49.0)
55
+ parallel (~> 1.10)
56
+ parser (>= 2.3.3.1, < 3.0)
57
+ powerpack (~> 0.1)
58
+ rainbow (>= 1.99.1, < 3.0)
59
+ ruby-progressbar (~> 1.7)
60
+ unicode-display_width (~> 1.0, >= 1.0.1)
61
+ ruby-progressbar (1.8.1)
62
+ safe_yaml (1.0.4)
63
+ slop (3.6.0)
64
+ unicode-display_width (1.2.1)
65
+ values (1.8.0)
66
+ webmock (3.0.1)
67
+ addressable (>= 2.3.6)
68
+ crack (>= 0.3.2)
69
+ hashdiff
70
+
71
+ PLATFORMS
72
+ ruby
73
+
74
+ DEPENDENCIES
75
+ bundler (~> 1.14)
76
+ pry (~> 0.10.4)
77
+ rake (~> 10.0)
78
+ rspec (~> 3.0)
79
+ rspec-its (~> 1.2.0)
80
+ rspec_junit_formatter (~> 0.2.3)
81
+ rubocop (~> 0.49.0)
82
+ starling-ruby!
83
+ values (~> 1.8.0)
84
+ webmock (~> 3.0.1)
85
+
86
+ BUNDLED WITH
87
+ 1.15.0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Tim Rogers
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,87 @@
1
+ # Starling Ruby Library
2
+
3
+ The Starling Ruby library provides a simple, idiomatic interface to the [Starling Bank API](https://developer.starlingbank.com).
4
+
5
+ [![CircleCI](https://circleci.com/gh/timrogers/starling-ruby/tree/master.svg?style=svg)](https://circleci.com/gh/timrogers/starling-ruby/tree/master)
6
+
7
+ ## Getting started
8
+
9
+ Install the gem by adding it to your Gemfile, and then run `bundle`:
10
+
11
+ ```ruby
12
+ # The gem will be available on RubyGems soon 💎
13
+ gem 'starling-ruby', github: "timrogers/starling-ruby"
14
+ ```
15
+
16
+ You can now initialise the client, providing an access token, an optionally an environment (either `:sandbox` or `:production`, defaulting to :production):
17
+
18
+ ```ruby
19
+ starling = Starling::Client.new(
20
+ access_token: ENV['STARLING_ACCESS_TOKEN'],
21
+ # Omit the line below to use the default production environment
22
+ environment: :sandbox,
23
+ )
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ Once you've initialised a `Starling::Client`, it exposes a number of services (living
29
+ in `lib/starling/services`) which have methods calling out to the API, and returning
30
+ resources (found in `lib/starling/resources`).
31
+
32
+ Much of the API has not yet been implemented. Stay tuned for further updates ❤️
33
+
34
+ ### Check your balance
35
+
36
+ ```ruby
37
+ balance = starling.account_balance.get
38
+ puts "Your balance is #{balance.amount} #{balance.currency}!"
39
+ ```
40
+
41
+ ### Fetch information about your account
42
+
43
+ ```ruby
44
+ account = starling.account.get
45
+ puts "Your sort code is #{account.sort_code} and your account number is #{account.number}."
46
+ ```
47
+
48
+ ### List transactions
49
+
50
+ ```ruby
51
+ transaction = starling.transactions.list.first
52
+ puts "Your most recent transaction was for #{transaction.amount} on #{transaction.created}"
53
+ ```
54
+
55
+ ### Fetch a transaction by ID
56
+
57
+ ```ruby
58
+ transaction = starling.transactions.get("insert-uuid-here")
59
+ puts "Your transaction was for #{transaction.amount} on #{transaction.created}"
60
+ ```
61
+
62
+ ## Tests
63
+
64
+ The recommended way to run tests on the project is using CircleCI's local Docker testing
65
+ - this is the best way to make sure that what passes tests locally in development will
66
+ work when you push it and it runs through our automated CI.
67
+
68
+ ```bash
69
+ # Download the circleci binary (assuming /usr/local/bin is in your PATH)
70
+ curl -o /usr/local/bin/circleci https://circle-downloads.s3.amazonaws.com/releases/build_agent_wrapper/circleci && chmod +x /usr/local/bin/circleci
71
+
72
+ # Run the full CI process, including tests and Rubocop. You'll need Docker installed.
73
+ circleci build
74
+ ```
75
+
76
+ You can also run tests in your own environment by running `bundle exec rake`, and can
77
+ run Rubocop by running `bundle exec rubocop`.
78
+
79
+ ## Contributing
80
+
81
+ All contributions are welcome - just make a pull request, making sure you include tests
82
+ and write a good, informative commit message/pull request body.
83
+
84
+ Check out
85
+ [CODE_OF_CONDUCT.md](https://github.com/timrogers/starling-ruby/blob/master/CODE_OF_CONDUCT.md)
86
+ to learn about how we can best work together as an open source community to make the
87
+ Starling Ruby library as good as it can be.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'starling'
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(__FILE__)
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/lib/starling.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'starling/version'
2
+ require 'starling/client'
3
+ require 'starling/api_service'
4
+ require 'starling/request'
5
+ require 'starling/errors/base_error'
6
+ require 'starling/errors/api_error'
7
+ require 'starling/middlewares/raise_starling_errors'
8
+ require 'starling/services/base_service'
9
+ require 'starling/services/account_service'
10
+ require 'starling/services/account_balance_service'
11
+ require 'starling/services/transactions_service'
12
+ require 'starling/resources/base_resource'
13
+ require 'starling/resources/account_resource'
14
+ require 'starling/resources/account_balance_resource'
15
+ require 'starling/resources/transaction_resource'
16
+
17
+ module Starling
18
+ end
@@ -0,0 +1,78 @@
1
+ require 'faraday'
2
+
3
+ module Starling
4
+ class ApiService
5
+ DEFAULT_ADAPTER = :net_http
6
+ BASE_PATH = '/api/v1'.freeze
7
+
8
+ def initialize(base_url, options = {})
9
+ @access_token = options.fetch(:access_token)
10
+
11
+ http_adapter = options[:http_adapter] || [DEFAULT_ADAPTER]
12
+ connection_options = options[:connection_options]
13
+
14
+ @connection = Faraday.new(base_url, connection_options) do |faraday|
15
+ faraday.response(:raise_starling_errors)
16
+ faraday.adapter(*http_adapter)
17
+ end
18
+
19
+ user_provided_default_headers = options.fetch(:default_headers, {})
20
+ @headers = default_headers.merge(user_provided_default_headers)
21
+ end
22
+
23
+ def make_request(method, path, options = {})
24
+ raise ArgumentError, 'options must be a hash' unless options.is_a?(Hash)
25
+ options[:headers] ||= {}
26
+ options[:headers] = @headers.merge(options[:headers])
27
+
28
+ Request.new(@connection, method, build_path(path), options)
29
+ .make_request
30
+ end
31
+
32
+ private
33
+
34
+ def build_path(path)
35
+ "#{BASE_PATH}#{path}"
36
+ end
37
+
38
+ def default_headers
39
+ {
40
+ 'Authorization' => "Bearer #{@access_token}",
41
+ 'Accept' => 'application/json',
42
+ 'User-Agent' => user_agent
43
+ }
44
+ end
45
+
46
+ def user_agent
47
+ @user_agent ||=
48
+ begin
49
+ comment = [
50
+ "#{ruby_engine}/#{ruby_version}",
51
+ "#{RUBY_ENGINE}/#{interpreter_version}",
52
+ RUBY_PLATFORM.to_s,
53
+ "faraday/#{Faraday::VERSION}"
54
+ ]
55
+
56
+ "#{gem_info} #{comment.join(' ')}"
57
+ end
58
+ end
59
+
60
+ def gem_info
61
+ return 'starling-ruby' unless defined?(Starling::VERSION)
62
+ "starling-ruby/v#{Starling::VERSION}"
63
+ end
64
+
65
+ def ruby_engine
66
+ defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
67
+ end
68
+
69
+ def ruby_version
70
+ return RUBY_VERSION unless defined?(RUBY_PATCHLEVEL)
71
+ RUBY_VERSION + "p#{RUBY_PATCHLEVEL}"
72
+ end
73
+
74
+ def interpreter_version
75
+ defined?(JRUBY_VERSION) ? JRUBY_VERSION : RUBY_VERSION
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,40 @@
1
+ require 'uri'
2
+
3
+ module Starling
4
+ class Client
5
+ ENVIRONMENT_BASE_URLS = {
6
+ production: 'https://api.starlingbank.com',
7
+ sandbox: 'https://api-sandbox.starlingbank.com'
8
+ }.freeze
9
+
10
+ def initialize(options = {})
11
+ raise ArgumentError, 'access_token must be provided' unless options[:access_token]
12
+
13
+ environment = options.delete(:environment) { :production }
14
+
15
+ @api_service = ApiService.new(fetch_base_url_for_environment(environment),
16
+ options)
17
+ end
18
+
19
+ def account
20
+ Services::AccountService.new(@api_service)
21
+ end
22
+
23
+ def account_balance
24
+ Services::AccountBalanceService.new(@api_service)
25
+ end
26
+
27
+ def transactions
28
+ Services::TransactionsService.new(@api_service)
29
+ end
30
+
31
+ private
32
+
33
+ def fetch_base_url_for_environment(environment)
34
+ ENVIRONMENT_BASE_URLS.fetch(environment) do
35
+ raise ArgumentError, "#{environment} is not a valid environment, must be one " \
36
+ "of #{ENVIRONMENT_BASE_URLS.keys.join(', ')}"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ require 'json'
2
+
3
+ module Starling
4
+ module Errors
5
+ class ApiError < BaseError
6
+ def message
7
+ return super unless json?
8
+ return super if error.nil? || error_description.nil?
9
+
10
+ "#{status}: #{error_description} (#{error})"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,48 @@
1
+ require 'json'
2
+
3
+ module Starling
4
+ module Errors
5
+ class BaseError < StandardError
6
+ extend Forwardable
7
+
8
+ def initialize(env)
9
+ @env = env
10
+ end
11
+
12
+ def_delegator :@env, :status
13
+ def_delegator :@env, :body
14
+
15
+ def message
16
+ message = status.to_s
17
+ message += ": #{body}" if body
18
+
19
+ message
20
+ end
21
+
22
+ alias to_s message
23
+
24
+ def error
25
+ return unless json?
26
+ parsed_body['error']
27
+ end
28
+
29
+ def error_description
30
+ return unless json?
31
+ parsed_body['error_description']
32
+ end
33
+
34
+ def parsed_body
35
+ return if body.nil?
36
+ JSON.parse(body)
37
+ rescue JSON::ParserError
38
+ nil
39
+ end
40
+
41
+ private
42
+
43
+ def json?
44
+ !parsed_body.nil?
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ module Starling
2
+ module Middlewares
3
+ class RaiseStarlingErrors < Faraday::Response::Middleware
4
+ ERROR_STATUSES = 400..599
5
+
6
+ def on_complete(env)
7
+ return unless !json?(env) || ERROR_STATUSES.include?(env.status)
8
+ raise Errors::ApiError, env
9
+ end
10
+
11
+ private
12
+
13
+ def json?(env)
14
+ content_type = env.response_headers['Content-Type'] ||
15
+ env.response_headers['content-type'] || ''
16
+
17
+ content_type.include?('application/json')
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ Faraday::Response.register_middleware(
24
+ raise_starling_errors: Starling::Middlewares::RaiseStarlingErrors
25
+ )
@@ -0,0 +1,40 @@
1
+ module Starling
2
+ class Request
3
+ def initialize(connection, method, path, options)
4
+ @connection = connection
5
+ @method = method
6
+ @path = path
7
+ @headers = options.delete(:headers) || {}
8
+ @options = options
9
+ @request_body = request_body
10
+ @request_query = request_query
11
+
12
+ return unless @request_body.is_a?(Hash)
13
+ @request_body = @request_body.to_json
14
+ @headers['Content-Type'] ||= 'application/json'
15
+ end
16
+
17
+ def make_request
18
+ @connection.send(@method) do |request|
19
+ request.url @path
20
+ request.body = @request_body
21
+ request.params = @request_query
22
+ request.headers.merge!(@headers)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def request_body
29
+ return @options.fetch(:params, {}) if %i[post put delete].include?(@method)
30
+
31
+ nil
32
+ end
33
+
34
+ def request_query
35
+ return @options.fetch(:params, {}) if @method == :get
36
+
37
+ {}
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ module Starling
2
+ module Resources
3
+ class AccountBalanceResource < BaseResource
4
+ def accepted_overdraft
5
+ parsed_data['acceptedOverdraft']
6
+ end
7
+
8
+ def amount
9
+ parsed_data['amount']
10
+ end
11
+
12
+ def available_to_spend
13
+ parsed_data['availableToSpend']
14
+ end
15
+
16
+ def cleared_balance
17
+ parsed_data['clearedBalance']
18
+ end
19
+
20
+ def currency
21
+ parsed_data['currency']
22
+ end
23
+
24
+ def effective_balance
25
+ parsed_data['effectiveBalance']
26
+ end
27
+
28
+ def pending_transactions
29
+ parsed_data['pendingTransactions']
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ module Starling
2
+ module Resources
3
+ class AccountResource < BaseResource
4
+ def created_at
5
+ present_datetime(parsed_data['createdAt'])
6
+ end
7
+
8
+ def currency
9
+ parsed_data['currency']
10
+ end
11
+
12
+ def iban
13
+ parsed_data['iban']
14
+ end
15
+
16
+ def bic
17
+ parsed_data['bic']
18
+ end
19
+
20
+ def id
21
+ parsed_data['id']
22
+ end
23
+
24
+ def name
25
+ parsed_data['name']
26
+ end
27
+
28
+ def number
29
+ parsed_data['number']
30
+ end
31
+
32
+ def sort_code
33
+ parsed_data['sortCode']
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ require 'json'
2
+ require 'time'
3
+
4
+ module Starling
5
+ module Resources
6
+ class BaseResource
7
+ # A resource can be instantiated with either a Faraday::Response, including a
8
+ # #body, or with a Hash pre-parsed from JSON
9
+ def initialize(response: nil, parsed_data: nil)
10
+ if response.nil? && parsed_data.nil?
11
+ raise ArgumentError, 'Either response or parsed_data must be provided to ' \
12
+ 'instantiate a resource'
13
+ end
14
+
15
+ @response = response
16
+ @parsed_data = parsed_data
17
+ end
18
+
19
+ private
20
+
21
+ def present_datetime(iso8601_string)
22
+ Time.parse(iso8601_string)
23
+ end
24
+
25
+ # Some strings returned by the API are specified as Enum types, with a specified
26
+ # set of possible values. These are best represented in Ruby as symbols (e.g.
27
+ # the Transaction API's `source` can have the value MASTER_CARD, which will become
28
+ # :master_card.
29
+ def symbolize_enum_string(enum_string)
30
+ enum_string.downcase.to_sym
31
+ end
32
+
33
+ def parsed_data
34
+ @parsed_data ||= JSON.parse(@response.body)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ module Starling
2
+ module Services
3
+ class AccountBalanceService < BaseService
4
+ def get(options = {})
5
+ response = @api_service.make_request(:get, '/accounts/balance', options)
6
+ resource.new(response: response)
7
+ end
8
+
9
+ private
10
+
11
+ def resource
12
+ Resources::AccountBalanceResource
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ module Starling
2
+ module Services
3
+ class AccountService < BaseService
4
+ def get(options = {})
5
+ resource.new(response: @api_service.make_request(:get, '/accounts', options))
6
+ end
7
+
8
+ def balance
9
+ Services::AccountBalanceService.new(@api_service)
10
+ end
11
+
12
+ private
13
+
14
+ def resource
15
+ Resources::AccountResource
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ require 'json'
2
+
3
+ module Starling
4
+ module Services
5
+ class BaseService
6
+ def initialize(api_service)
7
+ @api_service = api_service
8
+ end
9
+
10
+ private
11
+
12
+ def build_collection(response, key:, resource:)
13
+ JSON.parse(response.body)
14
+ .fetch('_embedded', {})
15
+ .fetch(key, []).map do |parsed_data|
16
+ resource.new(parsed_data: parsed_data)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Starling
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'starling/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'starling-ruby'
9
+ spec.version = Starling::VERSION
10
+ spec.authors = ['Tim Rogers']
11
+ spec.email = ['tim@gocardless.com']
12
+
13
+ spec.summary = 'A gem for interfacing with the Starling Bank API'
14
+ spec.homepage = 'https://github.com/timrogers/starling-ruby'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'faraday', ['>= 0.8.9', '< 0.10']
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.14'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.0'
27
+ spec.add_development_dependency 'rspec-its', '~> 1.2.0'
28
+ spec.add_development_dependency 'rubocop', '~> 0.49.0'
29
+ spec.add_development_dependency 'webmock', '~> 3.0.1'
30
+ spec.add_development_dependency 'pry', '~> 0.10.4'
31
+ spec.add_development_dependency 'values', '~> 1.8.0'
32
+ spec.add_development_dependency 'rspec_junit_formatter', '~> 0.2.3'
33
+ end
metadata ADDED
@@ -0,0 +1,219 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: starling-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tim Rogers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.9
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '0.10'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 0.8.9
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.10'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.14'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.14'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '10.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '10.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec-its
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 1.2.0
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 1.2.0
89
+ - !ruby/object:Gem::Dependency
90
+ name: rubocop
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: 0.49.0
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: 0.49.0
103
+ - !ruby/object:Gem::Dependency
104
+ name: webmock
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 3.0.1
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: 3.0.1
117
+ - !ruby/object:Gem::Dependency
118
+ name: pry
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: 0.10.4
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: 0.10.4
131
+ - !ruby/object:Gem::Dependency
132
+ name: values
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: 1.8.0
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: 1.8.0
145
+ - !ruby/object:Gem::Dependency
146
+ name: rspec_junit_formatter
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: 0.2.3
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: 0.2.3
159
+ description:
160
+ email:
161
+ - tim@gocardless.com
162
+ executables: []
163
+ extensions: []
164
+ extra_rdoc_files: []
165
+ files:
166
+ - ".circleci/config.yml"
167
+ - ".gitignore"
168
+ - ".rspec"
169
+ - ".rubocop.yml"
170
+ - CHANGELOG.md
171
+ - CODE_OF_CONDUCT.md
172
+ - Gemfile
173
+ - Gemfile.lock
174
+ - LICENSE.txt
175
+ - README.md
176
+ - Rakefile
177
+ - bin/console
178
+ - bin/setup
179
+ - lib/starling.rb
180
+ - lib/starling/api_service.rb
181
+ - lib/starling/client.rb
182
+ - lib/starling/errors/api_error.rb
183
+ - lib/starling/errors/base_error.rb
184
+ - lib/starling/middlewares/raise_starling_errors.rb
185
+ - lib/starling/request.rb
186
+ - lib/starling/resources/account_balance_resource.rb
187
+ - lib/starling/resources/account_resource.rb
188
+ - lib/starling/resources/base_resource.rb
189
+ - lib/starling/services/account_balance_service.rb
190
+ - lib/starling/services/account_service.rb
191
+ - lib/starling/services/base_service.rb
192
+ - lib/starling/version.rb
193
+ - starling-ruby.gemspec
194
+ homepage: https://github.com/timrogers/starling-ruby
195
+ licenses:
196
+ - MIT
197
+ metadata: {}
198
+ post_install_message:
199
+ rdoc_options: []
200
+ require_paths:
201
+ - lib
202
+ required_ruby_version: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
207
+ required_rubygems_version: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
212
+ requirements: []
213
+ rubyforge_project:
214
+ rubygems_version: 2.6.11
215
+ signing_key:
216
+ specification_version: 4
217
+ summary: A gem for interfacing with the Starling Bank API
218
+ test_files: []
219
+ has_rdoc: