freckle 0.0.1

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.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rest-client", "1.6.1"
4
+ gem "json", "1.4.6"
5
+
6
+ group :test do
7
+ gem "fakeweb", "1.3.0"
8
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,16 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ fakeweb (1.3.0)
5
+ json (1.4.6)
6
+ mime-types (1.16)
7
+ rest-client (1.6.1)
8
+ mime-types (>= 1.16)
9
+
10
+ PLATFORMS
11
+ ruby
12
+
13
+ DEPENDENCIES
14
+ fakeweb (= 1.3.0)
15
+ json (= 1.4.6)
16
+ rest-client (= 1.6.1)
data/README.textile ADDED
@@ -0,0 +1,41 @@
1
+ h1. Freckle
2
+
3
+ A simple RestClient-based Let's Freckle API client.
4
+
5
+ Only supports reading data.
6
+
7
+ h2. Example
8
+
9
+ Freckle.establish_conection(:account => "apitest",
10
+ :token => "lx3gi6pxdjtjn57afp8c2bv1me7g89j")
11
+
12
+ project = Freckle::Project.all.first
13
+ project.entries
14
+
15
+ h2. TODO
16
+
17
+ * CRUD
18
+
19
+ h2. License
20
+
21
+ The MIT License
22
+
23
+ Copyright (c) 2010 Matt Todd.
24
+
25
+ Permission is hereby granted, free of charge, to any person obtaining a copy
26
+ of this software and associated documentation files (the "Software"), to deal
27
+ in the Software without restriction, including without limitation the rights
28
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29
+ copies of the Software, and to permit persons to whom the Software is
30
+ furnished to do so, subject to the following conditions:
31
+
32
+ The above copyright notice and this permission notice shall be included in
33
+ all copies or substantial portions of the Software.
34
+
35
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << "test"
5
+ t.test_files = FileList['test/*_test.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ task :default => :test
data/lib/freckle.rb ADDED
@@ -0,0 +1,110 @@
1
+ # http://github.com/madrobby/freckle-apidocs
2
+
3
+ require 'logger'
4
+
5
+ require 'rest_client'
6
+ require 'json'
7
+
8
+ class Freckle
9
+ def initialize(options = {})
10
+ @account = options.delete(:account)
11
+ @token = options.delete(:token)
12
+ @logger = options.delete(:logger) || Logger.new(STDOUT)
13
+ RestClient.log = @logger
14
+ @connection = RestClient::Resource.new( "http://#{@account}.letsfreckle.com/api",
15
+ :headers => {"X-FreckleToken" => @token} )
16
+ end
17
+
18
+ def self.establish_connection(options)
19
+ @connection = new(options)
20
+ end
21
+ def self.connection
22
+ return @connection if @connection
23
+ raise RuntimeError, "Freckle.establish_connection with :account and :token first"
24
+ end
25
+
26
+ ### Query Methods
27
+
28
+ def users(options = {})
29
+ Array(JSON.parse(@connection['/users.json'].get)).flatten.map do |user|
30
+ User.new(user['user'], self)
31
+ end
32
+ end
33
+
34
+ def projects(options = {})
35
+ Array(JSON.parse(@connection['/projects.json'].get)).flatten.map do |project|
36
+ Project.new(project['project'], self)
37
+ end
38
+ end
39
+
40
+ # people: comma separated user ids
41
+ # projects: comma separated project ids
42
+ # tags: comma separated tag ids
43
+ # from: entries from this date
44
+ # to: entries to this date
45
+ # billable: true only shows billable entries; false only shows unbillable entries
46
+ def entries(options = {})
47
+ options.keys.each { |k| options["search[#{k}]"] = Array(options.delete(k)).flatten.join(',') }
48
+ Array(JSON.parse(@connection['/entries.json'].get(:params => options))).flatten.map do |entry|
49
+ Entry.new(entry['entry'], self)
50
+ end
51
+ end
52
+
53
+ ### Models
54
+
55
+ class Base
56
+ include Comparable
57
+ attr_reader :attributes
58
+ def initialize(attributes, connection = nil)
59
+ @connection = connection
60
+ @attributes = attributes
61
+ end
62
+ def id
63
+ @attributes['id']
64
+ end
65
+ def method_missing(method_name, *args)
66
+ return @attributes[method_name.to_s] if @attributes.key?(method_name.to_s)
67
+ super
68
+ end
69
+ def self.all(key = nil, options = {})
70
+ raise ArgumentError, "key is required" if key.nil?
71
+ Freckle.connection.send(key, options)
72
+ end
73
+ def self.first(options = {})
74
+ self.all(options).first
75
+ end
76
+ def <=>(other)
77
+ [self.class, self.id] <=> [other.class, other.id]
78
+ end
79
+ end
80
+
81
+ class User < Base
82
+ def self.all(options = {})
83
+ super(:users, options)
84
+ end
85
+ end
86
+
87
+ class Project < Base
88
+ def self.all(options = {})
89
+ super(:projects, options)
90
+ end
91
+ def entries(options = {})
92
+ projects = (options.delete(:projects) || [])
93
+ projects.push(self.id) unless projects.include?(self.id)
94
+ @connection.entries(options.merge(:projects => projects))
95
+ end
96
+ end
97
+
98
+ class Entry < Base
99
+ def self.all(options = {})
100
+ super(:entries, options)
101
+ end
102
+ def project(options = {})
103
+ @connection.projects(options).detect{ |project| project.id == self.project_id }
104
+ end
105
+ def user(options = {})
106
+ @connection.users(options).detect{ |user| user.id == self.user_id }
107
+ end
108
+ end
109
+
110
+ end
@@ -0,0 +1,14 @@
1
+ [
2
+ {"entry":{"created_at":"2010-07-22T10:02:00Z","billable":true,"minutes":240,"updated_at":"2010-07-22T10:02:00Z","recently_updated_at":"2010-07-22T10:02:00Z","project_id":8475,"url":null,"time_to":null,"id":311207,"date":"2010-07-24","user_id":5543,"formatted_description":"freckle restful api test","description_text":null,"time_from":null,"description":"freckle restful api test","invoiced_at":null,"project_invoice_id":null}},
3
+
4
+ {"entry":{"created_at":"2010-07-21T10:10:54Z","billable":true,"minutes":120,"updated_at":"2010-07-21T10:10:54Z","recently_updated_at":"2010-07-21T10:10:54Z","project_id":19618,"url":null,"time_to":null,"id":309459,"date":"2010-07-21","user_id":5543,"formatted_description":"freckle restful api test","description_text":null,"time_from":null,"description":"freckle restful api test","invoiced_at":null,"project_invoice_id":null}},
5
+ {"entry":{"created_at":"2010-07-21T09:48:13Z","billable":true,"minutes":15,"updated_at":"2010-07-21T09:48:13Z","recently_updated_at":"2010-07-21T09:48:13Z","project_id":19618,"url":null,"time_to":null,"id":309421,"date":"2010-07-21","user_id":5543,"formatted_description":"","description_text":null,"time_from":null,"description":"foobar","invoiced_at":null,"project_invoice_id":null}},
6
+
7
+ {"entry":{"created_at":"2010-07-22T09:34:09Z","billable":true,"minutes":180,"updated_at":"2010-07-22T09:34:09Z","recently_updated_at":"2010-07-22T09:34:09Z","project_id":19618,"url":null,"time_to":null,"id":311146,"date":"2010-07-21","user_id":5538,"formatted_description":"freckle restful api test","description_text":null,"time_from":null,"description":"freckle restful api test","invoiced_at":null,"project_invoice_id":null}},
8
+ {"entry":{"created_at":"2010-07-21T09:49:13Z","billable":true,"minutes":15,"updated_at":"2010-07-21T09:49:13Z","recently_updated_at":"2010-07-21T09:49:13Z","project_id":19618,"url":null,"time_to":null,"id":309427,"date":"2010-07-21","user_id":5538,"formatted_description":"","description_text":null,"time_from":null,"description":"foobar","invoiced_at":null,"project_invoice_id":null}},
9
+
10
+ {"entry":{"created_at":"2010-07-22T09:30:01Z","billable":true,"minutes":180,"updated_at":"2010-07-22T09:30:01Z","recently_updated_at":"2010-07-22T09:30:01Z","project_id":19618,"url":null,"time_to":null,"id":311144,"date":"2010-07-21","user_id":5543,"formatted_description":"freckle restful api test","description_text":null,"time_from":null,"description":"freckle restful api test","invoiced_at":null,"project_invoice_id":null}},
11
+ {"entry":{"created_at":"2010-06-17T18:25:34Z","billable":true,"minutes":120,"updated_at":"2010-06-17T18:25:34Z","recently_updated_at":"2010-06-17T18:25:34Z","project_id":19618,"url":null,"time_to":null,"id":259336,"date":"2010-06-17","user_id":5543,"formatted_description":null,"description_text":null,"time_from":null,"description":"","invoiced_at":null,"project_invoice_id":null}},
12
+
13
+ {"entry":{"created_at":"2010-06-17T18:26:34Z","billable":true,"minutes":120,"updated_at":"2010-06-17T18:26:34Z","recently_updated_at":"2010-06-17T18:26:34Z","project_id":8475,"url":null,"time_to":null,"id":259339,"date":"2010-06-17","user_id":5543,"formatted_description":"","description_text":null,"time_from":null,"description":"new tag, other tag","invoiced_at":null,"project_invoice_id":null}}
14
+ ]
@@ -0,0 +1,10 @@
1
+ [
2
+ {"entry":{"created_at":"2010-07-21T10:10:54Z","billable":true,"minutes":120,"updated_at":"2010-07-21T10:10:54Z","recently_updated_at":"2010-07-21T10:10:54Z","project_id":19618,"url":null,"time_to":null,"id":309459,"date":"2010-07-21","user_id":5543,"formatted_description":"freckle restful api test","description_text":null,"time_from":null,"description":"freckle restful api test","invoiced_at":null,"project_invoice_id":null}},
3
+ {"entry":{"created_at":"2010-07-21T09:48:13Z","billable":true,"minutes":15,"updated_at":"2010-07-21T09:48:13Z","recently_updated_at":"2010-07-21T09:48:13Z","project_id":19618,"url":null,"time_to":null,"id":309421,"date":"2010-07-21","user_id":5543,"formatted_description":"","description_text":null,"time_from":null,"description":"foobar","invoiced_at":null,"project_invoice_id":null}},
4
+
5
+ {"entry":{"created_at":"2010-07-22T09:34:09Z","billable":true,"minutes":180,"updated_at":"2010-07-22T09:34:09Z","recently_updated_at":"2010-07-22T09:34:09Z","project_id":19618,"url":null,"time_to":null,"id":311146,"date":"2010-07-21","user_id":5538,"formatted_description":"freckle restful api test","description_text":null,"time_from":null,"description":"freckle restful api test","invoiced_at":null,"project_invoice_id":null}},
6
+ {"entry":{"created_at":"2010-07-21T09:49:13Z","billable":true,"minutes":15,"updated_at":"2010-07-21T09:49:13Z","recently_updated_at":"2010-07-21T09:49:13Z","project_id":19618,"url":null,"time_to":null,"id":309427,"date":"2010-07-21","user_id":5538,"formatted_description":"","description_text":null,"time_from":null,"description":"foobar","invoiced_at":null,"project_invoice_id":null}},
7
+
8
+ {"entry":{"created_at":"2010-07-22T09:30:01Z","billable":true,"minutes":180,"updated_at":"2010-07-22T09:30:01Z","recently_updated_at":"2010-07-22T09:30:01Z","project_id":19618,"url":null,"time_to":null,"id":311144,"date":"2010-07-21","user_id":5543,"formatted_description":"freckle restful api test","description_text":null,"time_from":null,"description":"freckle restful api test","invoiced_at":null,"project_invoice_id":null}},
9
+ {"entry":{"created_at":"2010-06-17T18:25:34Z","billable":true,"minutes":120,"updated_at":"2010-06-17T18:25:34Z","recently_updated_at":"2010-06-17T18:25:34Z","project_id":19618,"url":null,"time_to":null,"id":259336,"date":"2010-06-17","user_id":5543,"formatted_description":null,"description_text":null,"time_from":null,"description":"","invoiced_at":null,"project_invoice_id":null}}
10
+ ]
@@ -0,0 +1,4 @@
1
+ [
2
+ {"entry":{"created_at":"2010-07-22T09:34:09Z","billable":true,"minutes":180,"updated_at":"2010-07-22T09:34:09Z","recently_updated_at":"2010-07-22T09:34:09Z","project_id":19618,"url":null,"time_to":null,"id":311146,"date":"2010-07-21","user_id":5538,"formatted_description":"freckle restful api test","description_text":null,"time_from":null,"description":"freckle restful api test","invoiced_at":null,"project_invoice_id":null}},
3
+ {"entry":{"created_at":"2010-07-21T09:49:13Z","billable":true,"minutes":15,"updated_at":"2010-07-21T09:49:13Z","recently_updated_at":"2010-07-21T09:49:13Z","project_id":19618,"url":null,"time_to":null,"id":309427,"date":"2010-07-21","user_id":5538,"formatted_description":"","description_text":null,"time_from":null,"description":"foobar","invoiced_at":null,"project_invoice_id":null}}
4
+ ]
@@ -0,0 +1,4 @@
1
+ [
2
+ {"project":{"name":"A new Company","created_at":"2010-06-08T00:53:20Z","billable":true,"updated_at":"2010-06-08T00:53:20Z","account_id":5039,"id":19618,"enabled":true,"user_id":null,"budget":null,"stepping":15,"invoice_recipient_details":null}},
3
+ {"project":{"name":"Fixture Company","created_at":"2009-10-16T09:04:50Z","billable":true,"updated_at":"2009-11-01T14:54:39Z","account_id":5039,"id":8475,"enabled":true,"user_id":null,"budget":null,"stepping":5,"invoice_recipient_details":null}}
4
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ {"user":{"id":5538,"last_name":"Freckle","time_format":"fraction","login":"admin","first_name":"Lets","email":"apitestadmin@letsfreckle.com"}}
3
+ ]
@@ -0,0 +1,91 @@
1
+ require 'test_helper'
2
+
3
+ class FreckleTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @logger = Logger.new(File.join(File.dirname(__FILE__), 'test.log'))
7
+ @connection = Freckle.new(:account => @account = "test",
8
+ :token => @token = "test",
9
+ :logger => @logger)
10
+ end
11
+
12
+ def teardown
13
+ Freckle.send(:instance_variable_set, :@connection, nil)
14
+ end
15
+
16
+ ### Query Methods
17
+
18
+ def test_users
19
+ assert !@connection.users.empty?
20
+ end
21
+
22
+ def test_projects
23
+ assert !@connection.projects.empty?
24
+ end
25
+
26
+ def test_entries
27
+ assert !@connection.entries.empty?
28
+ end
29
+
30
+ ### Models
31
+
32
+ def test_users_return_user_model
33
+ assert user = @connection.users.first
34
+ assert user.is_a?(Freckle::User)
35
+ end
36
+
37
+ def test_projects_return_project_model
38
+ assert project = @connection.projects.first
39
+ assert project.is_a?(Freckle::Project)
40
+ end
41
+
42
+ def test_entries_return_entry_model
43
+ assert entry = @connection.entries.first
44
+ assert entry.is_a?(Freckle::Entry)
45
+ end
46
+
47
+ def test_user_model_attributes_are_available_as_methods
48
+ assert user = @connection.users.first
49
+ assert_equal user.id, user.attributes['id']
50
+ assert_equal user.first_name, user.attributes['first_name']
51
+ assert_equal user.last_name, user.attributes['last_name']
52
+ end
53
+
54
+ ### Associations
55
+
56
+ def test_project_model_can_query_for_its_entries
57
+ assert project = @connection.projects.first
58
+ project.entries.each do |entry|
59
+ assert_equal project, entry.project
60
+ end
61
+ end
62
+
63
+ ### Filtering
64
+
65
+ def test_entries_can_filter_by_user
66
+ assert user = @connection.users.first
67
+ assert project = @connection.projects.first
68
+ @connection.entries(:people => [user.id]).each do |entry|
69
+ assert_equal user, entry.user
70
+ end
71
+ end
72
+
73
+ ### Singleton Connection
74
+
75
+ def test_user_all_without_established_connection_raises_error
76
+ assert_raise RuntimeError, /establish_connection/ do
77
+ Freckle::User.all
78
+ end
79
+ end
80
+
81
+ def test_user_all_matches_users
82
+ Freckle.establish_connection(:account => @account, :token => @token, :logger => @logger)
83
+ assert_equal @connection.users, Freckle::User.all
84
+ end
85
+
86
+ def test_user_first_matches_first_user
87
+ Freckle.establish_connection(:account => @account, :token => @token, :logger => @logger)
88
+ assert_equal @connection.users.first, Freckle::User.first
89
+ end
90
+
91
+ end
@@ -0,0 +1,17 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'freckle')
5
+
6
+ require 'test/unit'
7
+ require 'fakeweb'
8
+ FakeWeb.allow_net_connect = false
9
+
10
+ { "http://test.letsfreckle.com/api/users.json" => "users",
11
+ "http://test.letsfreckle.com/api/projects.json" => "projects",
12
+ "http://test.letsfreckle.com/api/entries.json" => "entries",
13
+ "http://test.letsfreckle.com/api/entries.json?search[projects]=19618" => "entries_for_project",
14
+ "http://test.letsfreckle.com/api/entries.json?search[people]=5538" => "entries_for_user"
15
+ }.each do |(uri, fixture)|
16
+ FakeWeb.register_uri(:get, uri, :body => File.read(File.join(File.dirname(__FILE__), "fixtures", "%s.json" % fixture)))
17
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: freckle
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Matt Todd
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-23 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rest-client
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - "="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 1
32
+ - 6
33
+ - 1
34
+ version: 1.6.1
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: json
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - "="
44
+ - !ruby/object:Gem::Version
45
+ hash: 11
46
+ segments:
47
+ - 1
48
+ - 4
49
+ - 6
50
+ version: 1.4.6
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: fakeweb
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - "="
60
+ - !ruby/object:Gem::Version
61
+ hash: 27
62
+ segments:
63
+ - 1
64
+ - 3
65
+ - 0
66
+ version: 1.3.0
67
+ type: :development
68
+ version_requirements: *id003
69
+ description: Let's Freckle API Client
70
+ email: mtodd@highgroove.com
71
+ executables: []
72
+
73
+ extensions: []
74
+
75
+ extra_rdoc_files: []
76
+
77
+ files:
78
+ - Gemfile
79
+ - Gemfile.lock
80
+ - README.textile
81
+ - Rakefile
82
+ - lib/freckle.rb
83
+ - test/fixtures/entries.json
84
+ - test/fixtures/entries_for_project.json
85
+ - test/fixtures/entries_for_user.json
86
+ - test/fixtures/projects.json
87
+ - test/fixtures/users.json
88
+ - test/freckle_test.rb
89
+ - test/test_helper.rb
90
+ has_rdoc: true
91
+ homepage: http://github.com/reddavis/freckly
92
+ licenses: []
93
+
94
+ post_install_message:
95
+ rdoc_options: []
96
+
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ requirements: []
118
+
119
+ rubyforge_project:
120
+ rubygems_version: 1.3.7
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: A Ruby client for the Let's Freckle API
124
+ test_files:
125
+ - test/freckle_test.rb
126
+ - test/test_helper.rb