letsfreckle-client 0.1.2 → 0.2.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.
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
+ require 'letsfreckle'
5
+
6
+ # Configure the API credentials.
7
+ LetsFreckle.configure do
8
+ account_host "apitest"
9
+ username "apitest@letsfreckle.com"
10
+ token "lx3gi6pxdjtjn57afp8c2bv1me7g89j"
11
+ end
12
+
13
+ puts "Fetch all entries:"
14
+ LetsFreckle::Entry.all.each do |entry|
15
+ puts "Entry #{entry.description} - #{entry.minutes} minutes"
16
+ end
17
+
18
+ puts "Search for entries from a specific start date and tags:"
19
+ LetsFreckle::Entry.find(:from => '2010-07-10', :tags => ['development', 'design']).each do |entry|
20
+ puts entry.description
21
+ end
22
+
23
+ puts "Fetch all projects and associated entries:"
24
+ LetsFreckle::Project.all.each do |project|
25
+ puts "Project #{project.name} has #{project.entries.size} entries"
26
+ end
27
+
28
+ puts "Fetch all tags and associated entries:"
29
+ LetsFreckle::Tag.all.each do |tag|
30
+ puts "Tag #{tag.name} has #{tag.entries.size} entries"
31
+ end
32
+
33
+ puts "Fetch all users and associated permissions:"
34
+ LetsFreckle::User.all.each do |user|
35
+ puts "User #{user.email} has following permissions: #{user.permissions}"
36
+ end
37
+
38
+
39
+
40
+
41
+
@@ -19,8 +19,11 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- s.add_dependency("httparty", ">= 0.7.8")
23
- s.add_dependency("hashie", ">= 1.0.0")
24
- s.add_development_dependency("rspec", "~> 2.5.0")
25
- s.add_development_dependency("webmock", "~> 1.6.2")
22
+ s.add_dependency 'hashie', '~> 1.1.0'
23
+ s.add_dependency 'faraday', '~> 0.7.4'
24
+ s.add_dependency 'faraday_middleware', '~> 0.7.0'
25
+ s.add_dependency 'multi_xml', '~> 0.2.0'
26
+ s.add_development_dependency "activesupport", "~> 2.3"
27
+ s.add_development_dependency 'rspec', '~> 2.5.0'
28
+ s.add_development_dependency 'webmock', '~> 1.6.2'
26
29
  end
@@ -0,0 +1,20 @@
1
+ module Faraday
2
+ class Request
3
+ class XML < Faraday::Middleware
4
+ dependency 'active_support/all'
5
+
6
+ def initialize(app, options={})
7
+ @app = app
8
+ @options = options
9
+ end
10
+
11
+ def call(env)
12
+ if env[:method] == :post
13
+ env[:body] = env[:body].to_xml(:root => env[:body].delete(:root))
14
+ puts env[:body]
15
+ end
16
+ @app.call(env)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module Faraday
2
+ class Response
3
+ # FlattenBody is a middleware that flattens the response body, and also extends
4
+ # Hashie::Mash instances with a module that allows them to work properly with DelegateClass.
5
+ class FlattenBody < Response::Middleware
6
+ dependency 'hashie/mash'
7
+
8
+ def parse(body)
9
+ return body unless body.respond_to?(:to_a)
10
+ flattened_response = body.to_a.flatten
11
+ flattened_response.keep_if { |item| ::Hashie::Mash === item }
12
+ flattened_response.map do |mash|
13
+ # extend so that #respond_to? works nicely with DelegateClass
14
+ mash.extend(LetsFreckle::Extensions::Mash)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Faraday
2
+ class Response
3
+ class VerifyStatus < Response::Middleware
4
+ def on_complete(env)
5
+ status = env[:status].to_i
6
+ case env[:method]
7
+ when :get
8
+ raise LetsFreckle::FetchError, "Fetch failed, HTTP error: #{status}" unless status == 200
9
+ when :post
10
+ raise LetsFreckle::CreateError, "Create failed, HTTP error: #{status}" unless status == 201
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+
@@ -1,7 +1,12 @@
1
- require 'hashie'
2
- require 'httparty'
3
1
  require 'delegate'
4
2
 
3
+ require 'hashie'
4
+ require 'faraday'
5
+ require 'faraday_middleware'
6
+ require 'faraday/request/xml'
7
+ require 'faraday/response/flatten_body'
8
+ require 'faraday/response/verify_status'
9
+
5
10
  require 'letsfreckle/version'
6
11
  require 'letsfreckle/extensions/mash'
7
12
  require 'letsfreckle/configuration'
@@ -2,38 +2,41 @@ module LetsFreckle
2
2
  # ClientResource should be extended by resource classes in order to gain
3
3
  # access to HTTP methods.
4
4
  module ClientResource
5
- API_URL = "https://%s.letsfreckle.com/api/%s.xml?token=%s"
6
5
 
7
- def fetch(resource, options = {})
8
- response = HTTParty.get(url(resource), :query => options, :format => :xml)
9
- verify!(response, 200) { raise FetchError, "Fetch failed, HTTP error: #{response.code}" }
10
- mashes = mashes_from_response(response)
11
- mashes.map { |m| new(m) }
6
+ def get(resource, options = {})
7
+ response = client.get do |request|
8
+ request.url relative_path_for(resource), options
9
+ end
10
+ response.body.map { |mash| new(mash) }
12
11
  end
13
12
 
14
13
  def post(resource, options = {})
15
- response = HTTParty.post(url(resource), :body => options, :format => :xml)
16
- verify!(response, 201) { raise CreateError, "Create failed, HTTP error: #{response.code}" }
14
+ client.post do |request|
15
+ request.url relative_path_for(resource)
16
+ request.headers['Content-Type'] = 'application/xml'
17
+ request.body = options
18
+ end
17
19
  end
18
20
 
19
- def url(resource)
20
- API_URL % [LetsFreckle.config.account_host, resource, LetsFreckle.config.token]
21
+ def base_api_url
22
+ "https://#{LetsFreckle.config.account_host}.letsfreckle.com"
21
23
  end
22
24
 
23
- private
24
-
25
- def verify!(response, code, &block)
26
- block.call unless response.code == code
25
+ def relative_path_for(resource)
26
+ "/api/#{resource}.xml?token=#{LetsFreckle.config.token}"
27
27
  end
28
28
 
29
- def mashes_from_response(response)
30
- return [] unless response.respond_to?(:to_a)
31
- flattened_response = response.to_a.flatten
32
- flattened_response.keep_if { |r| r.is_a?(Hash) }
33
- flattened_response.map do |h|
34
- m = Hashie::Mash.new(h)
35
- # extend so that #respond_to? works nicely with DelegateClass
36
- m.extend(Extensions::Mash)
29
+ private
30
+
31
+ def client
32
+ Faraday.new(:url => base_api_url) do |builder|
33
+ builder.use Faraday::Request::UrlEncoded
34
+ builder.use Faraday::Request::XML
35
+ builder.use Faraday::Response::FlattenBody
36
+ builder.use Faraday::Response::Mashify
37
+ builder.use Faraday::Response::ParseXml
38
+ builder.use Faraday::Response::VerifyStatus
39
+ builder.use Faraday::Adapter::NetHttp
37
40
  end
38
41
  end
39
42
  end
@@ -3,7 +3,7 @@ module LetsFreckle
3
3
  extend ClientResource
4
4
 
5
5
  def self.all
6
- fetch('entries')
6
+ get('entries')
7
7
  end
8
8
 
9
9
  # Fetches all entries. Supported options are:
@@ -14,7 +14,7 @@ module LetsFreckle
14
14
  # :to => '2012-01-01'
15
15
  # :billable => true/false
16
16
  def self.find(options = {})
17
- fetch('entries', searchable_options_from(options))
17
+ get('entries', searchable_options_from(options))
18
18
  end
19
19
 
20
20
  # Creates a new entry. Supported options are:
@@ -25,14 +25,16 @@ module LetsFreckle
25
25
  def self.create(options = {})
26
26
  raise ArgumentError, ':username config missing' unless LetsFreckle.config.username
27
27
  raise ArgumentError, ':minutes missing' unless options.has_key?(:minutes)
28
- post('entries', :entry => options.merge(:user => LetsFreckle.config.username))
28
+ post('entries', options.merge(:root => :entry, :user => LetsFreckle.config.username))
29
29
  end
30
30
 
31
31
  def self.searchable_options_from(options = {})
32
32
  options.each_with_object({}) do |(key, value), result|
33
33
  case value
34
- when Array then result["search[#{key}]"] = value.join(',')
35
- else result["search[#{key}]"] = value.to_s
34
+ when Array then
35
+ result["search[#{key}]"] = value.join(',')
36
+ else
37
+ result["search[#{key}]"] = value.to_s
36
38
  end
37
39
  end
38
40
  end
@@ -1,6 +1,5 @@
1
1
  module LetsFreckle
2
- class FetchError < StandardError
3
- end
4
- class CreateError < StandardError
5
- end
6
- end
2
+ class Error < StandardError; end
3
+ class FetchError < Error; end
4
+ class CreateError < Error; end
5
+ end
@@ -3,7 +3,7 @@ module LetsFreckle
3
3
  extend ClientResource
4
4
 
5
5
  def self.all
6
- fetch('projects')
6
+ get('projects')
7
7
  end
8
8
 
9
9
  def entries
@@ -3,7 +3,7 @@ module LetsFreckle
3
3
  extend ClientResource
4
4
 
5
5
  def self.all
6
- fetch('tags')
6
+ get('tags')
7
7
  end
8
8
 
9
9
  def entries
@@ -3,7 +3,7 @@ module LetsFreckle
3
3
  extend ClientResource
4
4
 
5
5
  def self.all
6
- fetch('users')
6
+ get('users')
7
7
  end
8
8
 
9
9
  def entries
@@ -1,3 +1,3 @@
1
1
  module Letsfreckle
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -9,7 +9,7 @@ describe LetsFreckle::Entry do
9
9
  token "secret"
10
10
  end
11
11
 
12
- stub_http_request(:get, LetsFreckle::Entry.url('entries')).to_return(:body => load_response('entries'))
12
+ stub_api_request('entries')
13
13
  entries = LetsFreckle::Entry.all
14
14
  entries.size.should == 2
15
15
 
@@ -9,7 +9,7 @@ describe LetsFreckle::Project do
9
9
  token "secret"
10
10
  end
11
11
 
12
- stub_http_request(:get, LetsFreckle::Project.url('projects')).to_return(:body => load_response('projects'))
12
+ stub_api_request('projects')
13
13
  projects = LetsFreckle::Project.all
14
14
  projects.size.should == 2
15
15
 
@@ -9,7 +9,7 @@ describe LetsFreckle::Tag do
9
9
  token "secret"
10
10
  end
11
11
 
12
- stub_http_request(:get, LetsFreckle::Tag.url('tags')).to_return(:body => load_response('tags'))
12
+ stub_api_request('tags')
13
13
  tags = LetsFreckle::Tag.all
14
14
  tags.size.should == 2
15
15
 
@@ -9,7 +9,7 @@ describe LetsFreckle::User do
9
9
  token "secret"
10
10
  end
11
11
 
12
- stub_http_request(:get, LetsFreckle::User.url('users')).to_return(:body => load_response('users'))
12
+ stub_api_request('users')
13
13
  users = LetsFreckle::User.all
14
14
  users.size.should == 2
15
15
 
@@ -2,4 +2,16 @@ module WebMockHelper
2
2
  def load_response(resource)
3
3
  File.read(File.join(File.dirname(__FILE__), '/../responses', "#{resource}.xml.response"))
4
4
  end
5
- end
5
+
6
+ def url_for_resource(resource)
7
+ "#{LetsFreckle::Entry.base_api_url}#{LetsFreckle::Entry.relative_path_for(resource)}"
8
+ end
9
+
10
+ def stub_api_request(resource)
11
+ stub_http_request(:get, url_for_resource(resource)).
12
+ with(:headers => { 'Accept' =>'*/*',
13
+ 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
14
+ 'User-Agent' =>'Ruby' }).
15
+ to_return(:body => load_response(resource))
16
+ end
17
+ end
metadata CHANGED
@@ -1,98 +1,112 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: letsfreckle-client
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 1
8
- - 2
9
- version: 0.1.2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Ryan LeCompte
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2011-07-25 00:00:00 -04:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: httparty
12
+ date: 2011-08-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: hashie
16
+ requirement: &70346356154000 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.1.0
22
+ type: :runtime
22
23
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
+ version_requirements: *70346356154000
25
+ - !ruby/object:Gem::Dependency
26
+ name: faraday
27
+ requirement: &70346356153180 !ruby/object:Gem::Requirement
24
28
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
- - 7
31
- - 8
32
- version: 0.7.8
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.7.4
33
33
  type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: hashie
37
34
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
35
+ version_requirements: *70346356153180
36
+ - !ruby/object:Gem::Dependency
37
+ name: faraday_middleware
38
+ requirement: &70346356152680 !ruby/object:Gem::Requirement
39
39
  none: false
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- segments:
44
- - 1
45
- - 0
46
- - 0
47
- version: 1.0.0
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 0.7.0
48
44
  type: :runtime
49
- version_requirements: *id002
50
- - !ruby/object:Gem::Dependency
51
- name: rspec
52
45
  prerelease: false
53
- requirement: &id003 !ruby/object:Gem::Requirement
46
+ version_requirements: *70346356152680
47
+ - !ruby/object:Gem::Dependency
48
+ name: multi_xml
49
+ requirement: &70346356152220 !ruby/object:Gem::Requirement
54
50
  none: false
55
- requirements:
51
+ requirements:
56
52
  - - ~>
57
- - !ruby/object:Gem::Version
58
- segments:
59
- - 2
60
- - 5
61
- - 0
53
+ - !ruby/object:Gem::Version
54
+ version: 0.2.0
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70346356152220
58
+ - !ruby/object:Gem::Dependency
59
+ name: activesupport
60
+ requirement: &70346356151760 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '2.3'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70346356151760
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: &70346356151260 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
62
76
  version: 2.5.0
63
77
  type: :development
64
- version_requirements: *id003
65
- - !ruby/object:Gem::Dependency
66
- name: webmock
67
78
  prerelease: false
68
- requirement: &id004 !ruby/object:Gem::Requirement
79
+ version_requirements: *70346356151260
80
+ - !ruby/object:Gem::Dependency
81
+ name: webmock
82
+ requirement: &70346356150740 !ruby/object:Gem::Requirement
69
83
  none: false
70
- requirements:
84
+ requirements:
71
85
  - - ~>
72
- - !ruby/object:Gem::Version
73
- segments:
74
- - 1
75
- - 6
76
- - 2
86
+ - !ruby/object:Gem::Version
77
87
  version: 1.6.2
78
88
  type: :development
79
- version_requirements: *id004
80
- description: Ruby client for letsfreckle.com API that supports entries, projects, tags, and users
81
- email:
89
+ prerelease: false
90
+ version_requirements: *70346356150740
91
+ description: Ruby client for letsfreckle.com API that supports entries, projects,
92
+ tags, and users
93
+ email:
82
94
  - lecompte@gmail.com
83
- executables: []
84
-
95
+ executables:
96
+ - run-examples
85
97
  extensions: []
86
-
87
98
  extra_rdoc_files: []
88
-
89
- files:
99
+ files:
90
100
  - .gitignore
91
101
  - Gemfile
92
102
  - LICENSE
93
103
  - README.rdoc
94
104
  - Rakefile
105
+ - bin/run-examples
95
106
  - letsfreckle-client.gemspec
107
+ - lib/faraday/request/xml.rb
108
+ - lib/faraday/response/flatten_body.rb
109
+ - lib/faraday/response/verify_status.rb
96
110
  - lib/letsfreckle.rb
97
111
  - lib/letsfreckle/client_resource.rb
98
112
  - lib/letsfreckle/configuration.rb
@@ -114,39 +128,31 @@ files:
114
128
  - spec/responses/users.xml.response
115
129
  - spec/spec_helper.rb
116
130
  - spec/support/webmock_helper.rb
117
- has_rdoc: true
118
131
  homepage: http://github.com/ryanlecompte/letsfreckle-client
119
132
  licenses: []
120
-
121
133
  post_install_message:
122
134
  rdoc_options: []
123
-
124
- require_paths:
135
+ require_paths:
125
136
  - lib
126
- required_ruby_version: !ruby/object:Gem::Requirement
137
+ required_ruby_version: !ruby/object:Gem::Requirement
127
138
  none: false
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- segments:
132
- - 0
133
- version: "0"
134
- required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
144
  none: false
136
- requirements:
137
- - - ">="
138
- - !ruby/object:Gem::Version
139
- segments:
140
- - 0
141
- version: "0"
145
+ requirements:
146
+ - - ! '>='
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
142
149
  requirements: []
143
-
144
150
  rubyforge_project: letsfreckle-client
145
- rubygems_version: 1.3.7
151
+ rubygems_version: 1.8.6.1
146
152
  signing_key:
147
153
  specification_version: 3
148
154
  summary: Ruby client for letsfreckle.com API
149
- test_files:
155
+ test_files:
150
156
  - spec/letsfreckle/configuration_spec.rb
151
157
  - spec/letsfreckle/entry_spec.rb
152
158
  - spec/letsfreckle/project_spec.rb