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 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.
@@ -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,2 @@
1
+ class FreckleApi::Group < FreckleApi::Model
2
+ end
@@ -0,0 +1,2 @@
1
+ class FreckleApi::Import < FreckleApi::Model
2
+ end
@@ -0,0 +1,3 @@
1
+ class FreckleApi::Invoice < FreckleApi::Model
2
+ coerce_key :invoice_date, coerce_to_date
3
+ 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,6 @@
1
+ class FreckleApi::Timer < FreckleApi::Model
2
+ coerce_key :state, Symbol
3
+ coerce_key :date, coerce_to_date
4
+ coerce_key :user, FreckleApi::User
5
+ coerce_key :project, FreckleApi::Project
6
+ end
@@ -0,0 +1,2 @@
1
+ class FreckleApi::User < FreckleApi::Model
2
+ end
@@ -0,0 +1,3 @@
1
+ class FreckleApi
2
+ VERSION = '0.1.0'
3
+ 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
@@ -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