freckle-api 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 +7 -0
- data/LICENSE +22 -0
- data/README.md +30 -0
- data/lib/freckle_api.rb +61 -0
- data/lib/freckle_api/coercions.rb +23 -0
- data/lib/freckle_api/group.rb +2 -0
- data/lib/freckle_api/import.rb +2 -0
- data/lib/freckle_api/invoice.rb +3 -0
- data/lib/freckle_api/model.rb +10 -0
- data/lib/freckle_api/project.rb +19 -0
- data/lib/freckle_api/timer.rb +6 -0
- data/lib/freckle_api/user.rb +2 -0
- data/lib/freckle_api/version.rb +3 -0
- data/spec/freckle_api_spec.rb +162 -0
- data/spec/spec_helper.rb +80 -0
- data/spec/support/fake_freckle.rb +35 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 62d0200c8b4beb472d2099eb324cae42d8f4396f
|
4
|
+
data.tar.gz: be9601b09316bc7c84605b974ad1edcffd2493fa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 41425bebaca7754292b57479ca4a99799f7f4f2ffb5742fccad9acae04a92ce5bc2f11dd9e9a550b22b983859c92a9d14515ffd88b39a73d7f51d658d31dbf2f
|
7
|
+
data.tar.gz: 9c330b1c4bca4160723ff36bf28b9f5200417062e9f334cb5de485e6ab330dda3717a2ceb83cd74a519a7b2a76d738053796f067fc5dc97d14e0dc9c243523a2
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Jamie Schembri
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# freckle-api
|
2
|
+
Freckle API client for v2.
|
3
|
+
|
4
|
+
This is still in development and not ready to use yet.
|
5
|
+
|
6
|
+
I intend to implement timing functonality only to begin with,
|
7
|
+
adding more management features later on. In order to get it to a
|
8
|
+
usable/useful state, the following needs to be done:
|
9
|
+
|
10
|
+
## TODO
|
11
|
+
|
12
|
+
There's a lot to do right now, but to get this API into a useful state
|
13
|
+
for users simply logging their time, the following is mandatory:
|
14
|
+
|
15
|
+
- Implement timer:
|
16
|
+
- start
|
17
|
+
- pause
|
18
|
+
- log
|
19
|
+
- discard
|
20
|
+
|
21
|
+
Longer-term TODO items are, in general order of importance:
|
22
|
+
|
23
|
+
- Implement entries
|
24
|
+
- Error handling, retry on fail.
|
25
|
+
- Rate limiting -- Freckle only allows two requests per second per IP, so consider
|
26
|
+
other users on the network as well. Goes with error handling.
|
27
|
+
- The entire v2 API that is currently available
|
28
|
+
- Better testing of requests
|
29
|
+
- DRYer tests
|
30
|
+
- Whatever other TODOs or FIXMEs in the codebase.
|
data/lib/freckle_api.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
require 'freckle_api/coercions.rb'
|
4
|
+
require 'freckle_api/model.rb'
|
5
|
+
require 'freckle_api/import.rb'
|
6
|
+
require 'freckle_api/group.rb'
|
7
|
+
require 'freckle_api/invoice.rb'
|
8
|
+
require 'freckle_api/user.rb'
|
9
|
+
require 'freckle_api/project.rb'
|
10
|
+
require 'freckle_api/timer.rb'
|
11
|
+
|
12
|
+
class FreckleApi
|
13
|
+
BASE_URI = URI('https://api.letsfreckle.com/v2').freeze
|
14
|
+
USER_AGENT = 'freckle-api' # TODO: make this flexible?
|
15
|
+
|
16
|
+
def initialize(api_key)
|
17
|
+
@api_key = api_key
|
18
|
+
end
|
19
|
+
|
20
|
+
def project(id)
|
21
|
+
Project.new(request :get, uri('projects', id))
|
22
|
+
end
|
23
|
+
|
24
|
+
def projects
|
25
|
+
request(:get, uri('projects')).map do |project|
|
26
|
+
Project.new(project)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def timer(project)
|
31
|
+
project_id = project.respond_to?(:id) ? project.id : project
|
32
|
+
|
33
|
+
Timer.new(request :get, uri('projects', project_id, 'timer'))
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def request(method, uri)
|
39
|
+
https = Net::HTTP.new(uri.host, uri.port).tap { |h| h.use_ssl = true }
|
40
|
+
|
41
|
+
response = https.request(http_class(method).new(uri.path, headers))
|
42
|
+
|
43
|
+
JSON.parse(response.body)
|
44
|
+
end
|
45
|
+
|
46
|
+
def http_class(method)
|
47
|
+
"Net::HTTP::#{method.to_s.capitalize}".constantize
|
48
|
+
end
|
49
|
+
|
50
|
+
def uri(*path)
|
51
|
+
URI.parse [BASE_URI, *path].join('/')
|
52
|
+
end
|
53
|
+
|
54
|
+
def headers
|
55
|
+
{
|
56
|
+
'Content-Type' => 'application/json',
|
57
|
+
'User-Agent' => USER_AGENT,
|
58
|
+
'X-FreckleToken' => @api_key
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Coercions should retain a proc, so that we can easily
|
2
|
+
# use them with Hashie's coercion methods.
|
3
|
+
#
|
4
|
+
# E.g.:
|
5
|
+
# coerce_key, :the_key, name_of_method_that_returns_proc
|
6
|
+
#
|
7
|
+
module FreckleApi::Coercions
|
8
|
+
def self.included(base)
|
9
|
+
base.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
# TODO: timezone based on local machine
|
14
|
+
# TODO: beware nil values
|
15
|
+
def coerce_to_datetime
|
16
|
+
DateTime.method(:parse).to_proc
|
17
|
+
end
|
18
|
+
|
19
|
+
def coerce_to_date
|
20
|
+
Date.method(:parse).to_proc
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
class FreckleApi::Model < Hash
|
4
|
+
include Hashie::Extensions::Coercion
|
5
|
+
include Hashie::Extensions::MergeInitializer
|
6
|
+
include Hashie::Extensions::MethodAccess
|
7
|
+
include Hashie::Extensions::IndifferentAccess
|
8
|
+
|
9
|
+
include FreckleApi::Coercions
|
10
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class FreckleApi::Project < FreckleApi::Model
|
2
|
+
coerce_key :group, FreckleApi::Group
|
3
|
+
coerce_key :import, FreckleApi::Import
|
4
|
+
coerce_key :invoices, Array[FreckleApi::Invoice]
|
5
|
+
coerce_key :participants, Array[FreckleApi::User]
|
6
|
+
|
7
|
+
coerce_key :created_at, coerce_to_datetime
|
8
|
+
coerce_key :updated_at, coerce_to_datetime
|
9
|
+
|
10
|
+
# entries is a method of hash, so we're aliasing
|
11
|
+
# it for now. Hashie's MethodWithIndirectAccess
|
12
|
+
# should take care of this, but it doesn't
|
13
|
+
# seem to work in this case, so we do it manually.
|
14
|
+
alias_method :__entries, :entries
|
15
|
+
|
16
|
+
def entries
|
17
|
+
self[:entries]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'freckle_api'
|
3
|
+
|
4
|
+
RSpec.describe FreckleApi do
|
5
|
+
let(:base_uri) { 'https://api.letsfreckle.com/v2' }
|
6
|
+
let(:api) { FreckleApi.new('valid_api_key') }
|
7
|
+
|
8
|
+
describe '#project' do
|
9
|
+
context 'given a valid project id' do
|
10
|
+
let(:project) { api.project(37_396) }
|
11
|
+
|
12
|
+
it 'returns the project' do
|
13
|
+
expect(project).to be_a FreckleApi::Project
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'contains the expected simple values' do
|
17
|
+
expect(project.id).to eq 37_396
|
18
|
+
expect(project.name).to eq 'Gear GmbH'
|
19
|
+
expect(project.billing_increment).to eq 10
|
20
|
+
expect(project.enabled).to eq true
|
21
|
+
expect(project.billable).to eq true
|
22
|
+
expect(project.color).to eq '#ff9898'
|
23
|
+
expect(project.url).to eq "#{base_uri}/projects/37396"
|
24
|
+
expect(project.minutes).to eq 180
|
25
|
+
expect(project.billable_minutes).to eq 120
|
26
|
+
expect(project.unbillable_minutes).to eq 60
|
27
|
+
expect(project.invoiced_minutes).to eq 120
|
28
|
+
expect(project.remaining_minutes).to eq 630
|
29
|
+
expect(project.budget_minutes).to eq 750
|
30
|
+
expect(project.entries).to eq 0
|
31
|
+
expect(project.entries_url).to eq "#{base_uri}/projects/37396/entries"
|
32
|
+
expect(project.expenses).to eq 0
|
33
|
+
expect(project.expenses_url).to eq "#{base_uri}/projects/37396/expenses"
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'contains the correct coerced timestamps' do
|
37
|
+
expected_timestamp = DateTime.new(2012, 1, 9, 8, 33, 29)
|
38
|
+
|
39
|
+
expect(project.created_at).to eq expected_timestamp
|
40
|
+
expect(project.updated_at).to eq expected_timestamp
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'contains the expected group' do
|
44
|
+
group = project.group
|
45
|
+
|
46
|
+
expect(group).to be_a FreckleApi::Group
|
47
|
+
expect(group.id).to eq 3768
|
48
|
+
expect(group.name).to eq 'Sprockets, Inc.'
|
49
|
+
expect(group.url).to eq "#{base_uri}/project_groups/3768"
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'contains the expected import' do
|
53
|
+
import = project.import
|
54
|
+
|
55
|
+
expect(import).to be_a FreckleApi::Import
|
56
|
+
expect(import.id).to eq 8910
|
57
|
+
expect(import.url).to eq "#{base_uri}/imports/8910"
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'contains the expected invoices' do
|
61
|
+
expect(project.invoices).to respond_to(:count)
|
62
|
+
expect(project.invoices.count).to eq 1
|
63
|
+
|
64
|
+
invoice = project.invoices.last
|
65
|
+
|
66
|
+
expect(invoice).to be_a FreckleApi::Invoice
|
67
|
+
|
68
|
+
expect(invoice.id).to eq 12_345_678
|
69
|
+
expect(invoice.reference).to eq 'AA001'
|
70
|
+
expect(invoice.invoice_date).to eq Date.new(2013, 7, 9)
|
71
|
+
expect(invoice.state).to eq 'unpaid'
|
72
|
+
expect(invoice.total_amount).to eq 189.33
|
73
|
+
expect(invoice.url).to eq "#{base_uri}/invoices/12345678"
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'contains the expected participants' do
|
77
|
+
expect(project.participants).to respond_to(:count)
|
78
|
+
expect(project.participants.count).to eq 1
|
79
|
+
|
80
|
+
participant = project.participants.last
|
81
|
+
|
82
|
+
expect(participant).to be_a FreckleApi::User
|
83
|
+
expect(participant.id).to eq 5538
|
84
|
+
expect(participant.email).to eq 'john.test@test.com'
|
85
|
+
expect(participant.first_name).to eq 'John'
|
86
|
+
expect(participant.last_name).to eq 'Test'
|
87
|
+
expect(participant.profile_image_url).to eq(
|
88
|
+
'https://api.letsfreckle.com/images/avatars/0000/0001/avatar.jpg')
|
89
|
+
expect(participant.url).to eq "#{base_uri}/users/5538"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#projects' do
|
95
|
+
# Just smoke test for now. We've tested enough
|
96
|
+
# of the individual project above.
|
97
|
+
let(:projects) { api.projects }
|
98
|
+
|
99
|
+
it 'returns a collection of projects' do
|
100
|
+
expect(projects).to respond_to(:count)
|
101
|
+
expect(projects.count).to eq 1
|
102
|
+
expect(projects.last).to be_a FreckleApi::Project
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '#timer' do
|
107
|
+
let(:timer) { api.timer(37_396) }
|
108
|
+
|
109
|
+
it 'returns the timer' do
|
110
|
+
expect(timer).to be_a FreckleApi::Timer
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'contains the expected simple values' do
|
114
|
+
expect(timer.id).to eq 123_456
|
115
|
+
expect(timer.state).to eq :running
|
116
|
+
expect(timer.seconds).to eq 180
|
117
|
+
expect(timer.description).to eq 'freckle work'
|
118
|
+
expect(timer.url).to eq "#{base_uri}/projects/37396/timer"
|
119
|
+
expect(timer.start_url).to eq "#{base_uri}/projects/37396/timer/start"
|
120
|
+
expect(timer.pause_url).to eq "#{base_uri}/projects/37396/timer/pause"
|
121
|
+
expect(timer.log_url).to eq "#{base_uri}/projects/37396/timer/log"
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'contains the coerced date' do
|
125
|
+
expect(timer.date).to eq Date.new(2013, 7, 9)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'contains the expected user' do
|
129
|
+
user = timer.user
|
130
|
+
|
131
|
+
expect(user).to be_a FreckleApi::User
|
132
|
+
expect(user.id).to eq 5_538
|
133
|
+
expect(user.email).to eq 'john.test@test.com'
|
134
|
+
expect(user.first_name).to eq 'John'
|
135
|
+
expect(user.last_name).to eq 'Test'
|
136
|
+
expect(user.profile_image_url).to eq(
|
137
|
+
'https://api.letsfreckle.com/images/avatars/0000/0001/avatar.jpg')
|
138
|
+
expect(user.url).to eq "#{base_uri}/users/5538"
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'contains the expected project' do
|
142
|
+
project = timer.project
|
143
|
+
|
144
|
+
expect(project).to be_a FreckleApi::Project
|
145
|
+
expect(project.id).to eq 37_396
|
146
|
+
expect(project.name).to eq 'Gear GmbH'
|
147
|
+
expect(project.billing_increment).to eq 10
|
148
|
+
expect(project.enabled).to eq true
|
149
|
+
expect(project.billable).to eq true
|
150
|
+
expect(project.color).to eq '#ff9898'
|
151
|
+
expect(project.url).to eq "#{base_uri}/projects/37396"
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'when passing an actual project' do
|
155
|
+
let(:timer) { api.timer(api.project(37_396)) }
|
156
|
+
|
157
|
+
it 'returns the timer' do
|
158
|
+
expect(timer).to be_a FreckleApi::Timer
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
require 'pry'
|
4
|
+
require 'pry-byebug'
|
5
|
+
require 'support/fake_freckle.rb'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.expect_with :rspec do |expectations|
|
9
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
10
|
+
# and `failure_message` of custom matchers include text for helper methods
|
11
|
+
# defined using `chain`, e.g.:
|
12
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
13
|
+
# # => "be bigger than 2 and smaller than 4"
|
14
|
+
# ...rather than:
|
15
|
+
# # => "be bigger than 2"
|
16
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
17
|
+
end
|
18
|
+
|
19
|
+
config.mock_with :rspec do |mocks|
|
20
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
21
|
+
# a real object. This is generally recommended, and will default to
|
22
|
+
# `true` in RSpec 4.
|
23
|
+
mocks.verify_partial_doubles = true
|
24
|
+
end
|
25
|
+
|
26
|
+
# These two settings work together to allow you to limit a spec run
|
27
|
+
# to individual examples or groups you care about by tagging them with
|
28
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
29
|
+
# get run.
|
30
|
+
config.filter_run :focus
|
31
|
+
config.run_all_when_everything_filtered = true
|
32
|
+
|
33
|
+
# Allows RSpec to persist some state between runs in order to support
|
34
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
35
|
+
# you configure your source control system to ignore this file.
|
36
|
+
config.example_status_persistence_file_path = 'spec/examples.txt'
|
37
|
+
|
38
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
39
|
+
# recommended. For more details, see:
|
40
|
+
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
41
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
42
|
+
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
43
|
+
config.disable_monkey_patching!
|
44
|
+
|
45
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
46
|
+
# be too noisy due to issues in dependencies.
|
47
|
+
config.warnings = false
|
48
|
+
|
49
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
50
|
+
# file, and it's useful to allow more verbose output when running an
|
51
|
+
# individual spec file.
|
52
|
+
if config.files_to_run.one?
|
53
|
+
# Use the documentation formatter for detailed output,
|
54
|
+
# unless a formatter has already been configured
|
55
|
+
# (e.g. via a command-line flag).
|
56
|
+
config.default_formatter = 'doc'
|
57
|
+
end
|
58
|
+
|
59
|
+
# Print the 10 slowest examples and example groups at the
|
60
|
+
# end of the spec run, to help surface which specs are running
|
61
|
+
# particularly slow.
|
62
|
+
config.profile_examples = 10
|
63
|
+
|
64
|
+
# Run specs in random order to surface order dependencies. If you find an
|
65
|
+
# order dependency and want to debug it, you can fix the order by providing
|
66
|
+
# the seed, which is printed after each run.
|
67
|
+
# --seed 1234
|
68
|
+
config.order = :random
|
69
|
+
|
70
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
71
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
72
|
+
# test failures related to randomization by passing the same `--seed` value
|
73
|
+
# as the one that triggered the failure.
|
74
|
+
Kernel.srand config.seed
|
75
|
+
|
76
|
+
config.before(:each) do
|
77
|
+
# Thanks https://robots.thoughtbot.com/how-to-stub-external-services-in-tests
|
78
|
+
stub_request(:any, %r{api.letsfreckle.com:443/v2}).to_rack(FakeFreckle)
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'sinatra/namespace'
|
3
|
+
|
4
|
+
class FakeFreckle < Sinatra::Base
|
5
|
+
register Sinatra::Namespace
|
6
|
+
|
7
|
+
set :port, 443
|
8
|
+
|
9
|
+
namespace '/v2' do
|
10
|
+
namespace '/projects' do
|
11
|
+
get do
|
12
|
+
json_response 200, 'projects'
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace '/:id' do
|
16
|
+
get do
|
17
|
+
json_response 200, 'projects/37396'
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/timer' do
|
21
|
+
json_response 200, 'projects/37396/timer'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def json_response(response_code, file_name)
|
30
|
+
content_type :json
|
31
|
+
status response_code
|
32
|
+
|
33
|
+
File.open("#{File.dirname(__FILE__)}/fixtures/#{file_name}.json", 'rb').read
|
34
|
+
end
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: freckle-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jamie Schembri
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10.1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '10.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.4'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: webmock
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.22'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.22'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry-byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sinatra
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.4'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.4'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sinatra-contrib
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.4'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.4'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: hashie
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.4'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.4'
|
111
|
+
description:
|
112
|
+
email: jamie@schembri.me
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- LICENSE
|
118
|
+
- README.md
|
119
|
+
- lib/freckle_api.rb
|
120
|
+
- lib/freckle_api/coercions.rb
|
121
|
+
- lib/freckle_api/group.rb
|
122
|
+
- lib/freckle_api/import.rb
|
123
|
+
- lib/freckle_api/invoice.rb
|
124
|
+
- lib/freckle_api/model.rb
|
125
|
+
- lib/freckle_api/project.rb
|
126
|
+
- lib/freckle_api/timer.rb
|
127
|
+
- lib/freckle_api/user.rb
|
128
|
+
- lib/freckle_api/version.rb
|
129
|
+
- spec/freckle_api_spec.rb
|
130
|
+
- spec/spec_helper.rb
|
131
|
+
- spec/support/fake_freckle.rb
|
132
|
+
homepage: http://github.com/shkm/freckle-api
|
133
|
+
licenses:
|
134
|
+
- MIT
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.4.5.1
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Client for Freckle's API V2.
|
156
|
+
test_files:
|
157
|
+
- spec/freckle_api_spec.rb
|
158
|
+
- spec/spec_helper.rb
|
159
|
+
- spec/support/fake_freckle.rb
|