starling-ruby 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: 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: