elmas 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: 2ad5e2f6e04bc4bbb0d56e770fae4bd99ada5fca
4
+ data.tar.gz: 37bbce0d0945e16dbfd38ec787cb0280324b183e
5
+ SHA512:
6
+ metadata.gz: 8019c2c6edbf2e50598dd5d671f9390e1cd5c89bcc923df4f62018598be77cdfddb1647082b6ff1695d3c5ff68b9cf4f3d135e5472c8c383641577890fcefa6d
7
+ data.tar.gz: f58ab43a5cb1884ec87acad7725dd3d10ce6bf900c9d68e0cff88c71581cde7d9d9eb6ba6a7c69e46ff341d62726d78b96b46db857e612432090051eac739f81
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .DS_Store
11
+ .env
12
+ .log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,52 @@
1
+ Documentation:
2
+ Enabled: false
3
+
4
+ StringLiterals:
5
+ EnforcedStyle: double_quotes
6
+
7
+ AllCops:
8
+ Include:
9
+ - '**/Rakefile'
10
+ - '**/config.ru'
11
+ Exclude:
12
+ - 'db/**/*'
13
+ - 'tmp/**/*'
14
+ - 'vendor/**/*'
15
+ - 'bin/**/*'
16
+ - 'log/**/*'
17
+ - 'spec/**/*'
18
+ - 'config/**/*'
19
+ RunRailsCops: true
20
+
21
+ Metrics/AbcSize:
22
+ Max: 30
23
+ Metrics/BlockNesting:
24
+ Max: 3
25
+ Metrics/ClassLength:
26
+ CountComments: false # count full line comments?
27
+ Max: 100
28
+ Metrics/CyclomaticComplexity:
29
+ Max: 6
30
+ Metrics/LineLength:
31
+ Max: 150
32
+ AllowURI: true
33
+ URISchemes:
34
+ - http
35
+ - https
36
+
37
+ Metrics/MethodLength:
38
+ CountComments: false # count full line comments?
39
+ Max: 13
40
+
41
+ Metrics/ParameterLists:
42
+ Max: 5
43
+ CountKeywordArgs: true
44
+
45
+ Metrics/PerceivedComplexity:
46
+ Max: 7
47
+ Style/PerlBackrefs:
48
+ Enabled: false
49
+ Lint/AmbiguousOperator:
50
+ Enabled: false
51
+ Rails/Delegate:
52
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
4
+ cache: bundler
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in elmas.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,11 @@
1
+ guard :rspec, cmd: 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { "spec" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
7
+ guard :rubocop, all_on_start: false, cli: ['--format', 'clang', '--rails'] do
8
+ watch(%r{^spec/.+_spec\.rb$})
9
+ watch(%r{^lib/(.+)\.rb$})
10
+ watch('spec/spec_helper.rb')
11
+ end
data/README.md ADDED
@@ -0,0 +1,149 @@
1
+ # Elmas
2
+
3
+ Elmas means diamond, but in this case it's an API wrapper for [Exact Online](https://developers.exactonline.com/).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'elmas'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install elmas
20
+
21
+ ## Usage
22
+
23
+ You have to have an Exact Online account and an app setup to connect with.
24
+
25
+ You have to set a few variables to make a connection possible. I'd suggest using environment variables set with [dotenv](https://github.com/bkeepers/dotenv) for that. (You can of course hardcode them, but that is not very secure :-) )
26
+
27
+
28
+ Then configure Elmas like this
29
+
30
+ ```ruby
31
+ Elmas.configure do |config|
32
+ config.client_id = ENV['CLIENT_ID']
33
+ config.client_secret = ENV['CLIENT_SECRET']
34
+ config.access_token = Elmas.authorize(ENV['EXACT_USER_NAME'], ENV['EXACT_PASSWORD']).access_token
35
+ end
36
+
37
+ #The client will now be authorized for 10 minutes,
38
+ # if there are requests the time will be reset,
39
+ # otherwise authorization should be called again.
40
+ unless Elmas.authorized?
41
+ Elmas.configure do |config|
42
+ config.access_token = Elmas.authorize(ENV['EXACT_USER_NAME'], ENV['EXACT_PASSWORD']).access_token
43
+ end
44
+ end
45
+ ```
46
+
47
+ To find a contact
48
+
49
+ ```ruby
50
+ contact = Elmas::Contact.new(id: "23445")
51
+ contact.find
52
+ # path = /crm/Contacts?$filter=ID eq guid'23445'
53
+ ```
54
+
55
+ To find a contact with specific filters
56
+ ```ruby
57
+ contact = Elmas::Contact.new(first_name: "Karel", id: "23")
58
+ contact.find_by(filters: [:first_name])
59
+ # path = /crm/Contacts?$filter=first_name eq 'Karel'
60
+ ```
61
+
62
+ To find contacts with an order and a filter
63
+ ```ruby
64
+ contact = Elmas::Contact.new(first_name: "Karel")
65
+ contact.find_by(filters: [:first_name], order_by: :first_name)
66
+ # path = /crm/Contacts?$order_by=first_name&$filter=first_name eq 'Karel'
67
+ ```
68
+
69
+ To find contacts with an order, a filter and selecting relationships
70
+ ```ruby
71
+ contact = Elmas::Contact.new(first_name: "Karel")
72
+ contact.find_by(filters: [:first_name], order_by: :first_name, select: [:invoice])
73
+ # path = /crm/Contacts?$select=invoice&$order_by=first_name&$filter=first_name eq 'Karel'
74
+ ```
75
+
76
+ So with find_by you can combine Filters, Select and OrderBy. For more information on this way of selecting data look here http://www.odata.org/
77
+ There's also a method find_all, which does a get without filters. You can however set the select and order by params.
78
+
79
+ To find all contacts
80
+ ```ruby
81
+ contact = Elmas::Contact.new
82
+ contact.find_all
83
+ # path = /crm/Contacts
84
+ ```
85
+
86
+ To find all contacts and order by first_name
87
+ ```ruby
88
+ contact = Elmas::Contact.new
89
+ contact.find_all(order_by: :first_name)
90
+ # path = /crm/Contacts?$order_by=first_name
91
+ ```
92
+
93
+ To find all contacts and select invoices and items
94
+ ```ruby
95
+ contact = Elmas::Contact.new
96
+ contact.find_all(select: [:invoice, :item])
97
+ # path = /crm/Contacts?$select=invoice,item
98
+ ```
99
+
100
+ To create a new contact
101
+
102
+ ```ruby
103
+ contact = Elmas::Contact.new(first_name: "Karel", last_name: "Appel", id: "2378712")
104
+ contact.save
105
+ ```
106
+
107
+ ###Divisions and Endpoints
108
+
109
+ Usually in the exact wrapper you need a division number, this one will be set on authorization checks (with `/Current/Me` endpoint). Sometimes you need to do a request without the division number, or even without the standard `/api/v1` endpoint. Like so:
110
+
111
+ ```ruby
112
+ response = Elmas.get('/api/oauth2/token', no_endpoint: true, no_division: true)
113
+ response = Elmas.get('/Current/Me', no_division: true)
114
+ ```
115
+
116
+ ## Development
117
+
118
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
119
+
120
+ 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` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
121
+
122
+ ## Contributing
123
+
124
+ 1. Fork it ( https://github.com/[my-github-username]/elmas/fork )
125
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
126
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
127
+ 4. Push to the branch (`git push origin my-new-feature`)
128
+ 5. Create a new Pull Request
129
+
130
+ ## Testing
131
+
132
+ We use Rspec for normal unit testing. We aim for coverage above 90%. Also the current suite should succeed when you commit something.
133
+ We use Rubocop for style checking, this should also succeed before you commit anything.
134
+
135
+ We're also experimenting with Mutation testing, which alters your code to test if your specs fail when there's faulty code. This is important when you
136
+ alter a vital part of the code, make sure the mutation percentage is higher than 80%. To run a part of the code with mutant run the follwing
137
+ `mutant --include lib/elmas --require elmas --use rspec Elmas::ClassYoureWorkingOn`
138
+
139
+ To test the vital classes run this
140
+ `mutant --include lib --require elmas --use rspec Elmas::Response Elmas::Client Elmas::Utils Elmas::Resource Elmas::Request Elmas::Parser Elmas::Config`
141
+ This will take a few minutes
142
+
143
+ When you're editing code it's advised you run guard, which watches file changes and automatically runs Rspec and Rubocop.
144
+
145
+ ## Hoppinger
146
+
147
+ This gem was created by [Hoppinger](http://www.hoppinger.com)
148
+
149
+ [![forthebadge](http://forthebadge.com/images/badges/built-with-ruby.svg)](http://www.hoppinger.com)
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :rubocop do
4
+ sh "rubocop"
5
+ end
6
+
7
+ task :rspec do
8
+ sh "rspec"
9
+ end
10
+
11
+ task default: [:rubocop, :rspec]
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "elmas"
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
+ require "dotenv"
15
+ Dotenv.load
16
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/elmas.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'elmas/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "elmas"
8
+ spec.authors = ["Marthyn"]
9
+ spec.email = ["MarthynOlthof@hoppinger.nl"]
10
+
11
+ spec.summary = %q{API wrapper for Exact Online}
12
+ spec.homepage = "https://www.hoppinger.com"
13
+ spec.licenses = %w(MIT)
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
16
+ spec.require_paths = %w(lib)
17
+ spec.version = Elmas::Version.to_s
18
+
19
+ spec.add_dependency "faraday", [">= 0.8", "< 0.10"]
20
+ spec.add_dependency "mechanize", "2.6.0"
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.9"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency("rspec", "~> 3.0")
25
+ spec.add_development_dependency("simplecov")
26
+ spec.add_development_dependency("simplecov-rcov")
27
+ spec.add_development_dependency("webmock", "~> 1.6")
28
+ spec.add_development_dependency("rubycritic", "~> 1.4.0")
29
+ spec.add_development_dependency("guard-rspec")
30
+ spec.add_development_dependency("guard-rubocop")
31
+ spec.add_development_dependency("mutant-rspec")
32
+ spec.add_development_dependency("dotenv")
33
+ end
data/jenkins.sh ADDED
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+ export RAILS_ENV=test
3
+ export COVERAGE=true
4
+
5
+ bundle install
6
+
7
+ if [[ -d coverage ]]; then
8
+ echo "Removing old coverage report"
9
+ rm -r coverage
10
+ fi
11
+
12
+ echo "--- Check style"
13
+
14
+ rubocop
15
+
16
+ if [[ $? -ne 0 ]]; then
17
+ echo "--- Style checks failed."
18
+ exit 1
19
+ fi
20
+
21
+ echo "--- Doing a static analysis"
22
+
23
+ rubycritic app lib config
24
+
25
+ echo "--- Running RSpec"
26
+
27
+ rspec --color spec --format progress --format html --out rspec.html
28
+ rspec=$?
29
+
30
+ if [[ $rspec -ne 0 ]]; then
31
+ echo "--- Some tests have failed."
32
+ exit 1
33
+ fi
data/lib/elmas.rb ADDED
@@ -0,0 +1,33 @@
1
+ require "elmas/version"
2
+ require "elmas/api"
3
+ require "elmas/config"
4
+ require "elmas/response"
5
+ require "elmas/client"
6
+ require "elmas/log"
7
+ require "elmas/resource"
8
+ require "elmas/resources/contact"
9
+ require "elmas/resources/invoice"
10
+ require "elmas/resources/journal"
11
+ require "elmas/resources/item"
12
+ require "elmas/resources/invoice_line"
13
+ require "elmas/resources/account"
14
+
15
+ module Elmas
16
+ extend Config
17
+ extend Log
18
+
19
+ def self.client(options = {})
20
+ Elmas::Client.new(options)
21
+ end
22
+
23
+ # Delegate to Elmas::Client
24
+ def self.method_missing(method, *args, &block)
25
+ super unless client.respond_to?(method)
26
+ client.send(method, *args, &block)
27
+ end
28
+
29
+ # Delegate to Elmas::Client
30
+ def self.respond_to?(method, include_all = false)
31
+ client.respond_to?(method, include_all) || super
32
+ end
33
+ end
data/lib/elmas/api.rb ADDED
@@ -0,0 +1,30 @@
1
+ require File.expand_path("../request", __FILE__)
2
+ require File.expand_path("../config", __FILE__)
3
+ require File.expand_path("../oauth", __FILE__)
4
+
5
+ module Elmas
6
+ # @private
7
+ class API
8
+ # @private
9
+ attr_accessor *(Config::VALID_OPTIONS_KEYS)
10
+
11
+ # Creates a new API
12
+ def initialize(options = {})
13
+ options = Elmas.options.merge(options)
14
+ Config::VALID_OPTIONS_KEYS.each do |key|
15
+ send("#{key}=", options[key])
16
+ end
17
+ end
18
+
19
+ def config
20
+ conf = {}
21
+ Config::VALID_OPTIONS_KEYS.each do |key|
22
+ conf[key] = send key
23
+ end
24
+ conf
25
+ end
26
+
27
+ include Request
28
+ include OAuth
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ require "faraday"
2
+
3
+ module Elmas
4
+ class Client < API
5
+ def connection
6
+ Faraday.new do |faraday|
7
+ faraday.adapter Faraday.default_adapter
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,96 @@
1
+ require "faraday"
2
+
3
+ module Elmas
4
+ module Config
5
+ # An array of valid keys in the options hash
6
+ VALID_OPTIONS_KEYS = [
7
+ :access_token,
8
+ :adapter,
9
+ :client_id,
10
+ :client_secret,
11
+ :connection_options,
12
+ :redirect_uri,
13
+ :response_format,
14
+ :user_agent,
15
+ :endpoint,
16
+ :division,
17
+ :base_url,
18
+ :refresh_token
19
+ ].freeze
20
+
21
+ # By default, don't set a user access token
22
+ DEFAULT_ACCESS_TOKEN = ""
23
+
24
+ DEFAULT_REFRESH_TOKEN = ""
25
+
26
+ # The adapter that will be used to connect if none is set
27
+ #
28
+ # @note The default faraday adapter is Net::HTTP.
29
+ DEFAULT_ADAPTER = Faraday.default_adapter
30
+
31
+ # By default, client id should be set in .env
32
+ DEFAULT_CLIENT_ID = ""
33
+
34
+ # By default, client secret should be set in .env
35
+ DEFAULT_CLIENT_SECRET = ""
36
+
37
+ # By default, don't set any connection options
38
+ DEFAULT_CONNECTION_OPTIONS = {}
39
+
40
+ DEFAULT_BASE_URL = "https://start.exactonline.nl"
41
+
42
+ # The endpoint that will be used to connect if none is set
43
+ DEFAULT_ENDPOINT = "api/v1".freeze
44
+
45
+ # the division code you want to connect with
46
+ DEFAULT_DIVISION = "797636"
47
+
48
+ # The response format appended to the path and sent in the 'Accept' header if none is set
49
+ #
50
+ DEFAULT_FORMAT = :json
51
+
52
+ DEFAULT_REDIRECT_URI = "https://www.getpostman.com/oauth2/callback"
53
+
54
+ # By default, don't set user agent
55
+ DEFAULT_USER_AGENT = nil
56
+
57
+ # An array of valid request/response formats
58
+ VALID_FORMATS = [:json].freeze
59
+
60
+ # @private
61
+ attr_accessor *(VALID_OPTIONS_KEYS)
62
+
63
+ # When this module is extended, set all configuration options to their default values
64
+ def self.extended(base)
65
+ base.reset
66
+ end
67
+
68
+ # Convenience method to allow configuration options to be set in a block
69
+ def configure
70
+ yield self
71
+ end
72
+
73
+ # Create a hash of options and their values
74
+ def options
75
+ VALID_OPTIONS_KEYS.inject({}) do |option, key|
76
+ option.merge!(key => send(key))
77
+ end
78
+ end
79
+
80
+ # Reset all configuration options to defaults
81
+ def reset
82
+ self.access_token = DEFAULT_ACCESS_TOKEN
83
+ self.adapter = DEFAULT_ADAPTER
84
+ self.client_id = DEFAULT_CLIENT_ID
85
+ self.client_secret = DEFAULT_CLIENT_SECRET
86
+ self.connection_options = DEFAULT_CONNECTION_OPTIONS
87
+ self.redirect_uri = DEFAULT_REDIRECT_URI
88
+ self.endpoint = DEFAULT_ENDPOINT
89
+ self.division = DEFAULT_DIVISION
90
+ self.base_url = DEFAULT_BASE_URL
91
+ self.response_format = DEFAULT_FORMAT
92
+ self.user_agent = DEFAULT_USER_AGENT
93
+ self.refresh_token = DEFAULT_REFRESH_TOKEN
94
+ end
95
+ end
96
+ end