harvested 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -6,10 +6,9 @@ gem 'json'
6
6
 
7
7
  group :development, :test do
8
8
  gem "rspec", "~> 2"
9
- gem 'ruby-debug', :platform => [:mri_18, :jruby]
10
- gem 'ruby-debug19', :platform => :mri_19, :require => 'ruby-debug'
11
9
  gem 'jruby-openssl', :platform => [:jruby], :require => false
12
10
  gem "webmock"
13
11
  gem "vcr"
14
12
  gem 'jeweler', :require => false
15
- end
13
+ gem 'debugger'
14
+ end
data/HISTORY CHANGED
@@ -1,3 +1,5 @@
1
+ 0.6.0 - August 22, 2012
2
+ * Replaces Dash with Mash
1
3
  0.5.3 - August 21, 2012
2
4
  * Adding new fields has_timesheet_2012_beta and timesheet_2012_beta_control_group to Users (thanks Aldric Giacomoni)
3
5
  0.5.2 - August 17, 2012
data/Rakefile CHANGED
@@ -21,7 +21,7 @@ end
21
21
  require 'rspec/core/rake_task'
22
22
  RSpec::Core::RakeTask.new(:spec)
23
23
 
24
- task :default => %w(spec features)
24
+ task :default => %w(spec)
25
25
 
26
26
  begin
27
27
  require 'yard'
@@ -38,6 +38,3 @@ task 'clean_remote' do
38
38
  require "spec/support/harvested_helpers"
39
39
  HarvestedHelpers.clean_remote
40
40
  end
41
-
42
- task :foo do
43
- end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.3
1
+ 0.6.0
data/harvested.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "harvested"
8
- s.version = "0.5.3"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Zach Moazeni"]
12
- s.date = "2012-08-21"
12
+ s.date = "2012-08-23"
13
13
  s.description = "Harvested wraps the Harvest API concisely without the use of Rails dependencies. More information about the Harvest API can be found on their website (http://www.getharvest.com/api). For support hit up the Mailing List (http://groups.google.com/group/harvested)"
14
14
  s.email = "zach.moazeni@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -113,35 +113,32 @@ Gem::Specification.new do |s|
113
113
  s.add_runtime_dependency(%q<hashie>, ["~> 1"])
114
114
  s.add_runtime_dependency(%q<json>, [">= 0"])
115
115
  s.add_development_dependency(%q<rspec>, ["~> 2"])
116
- s.add_development_dependency(%q<ruby-debug>, [">= 0"])
117
- s.add_development_dependency(%q<ruby-debug19>, [">= 0"])
118
116
  s.add_development_dependency(%q<jruby-openssl>, [">= 0"])
119
117
  s.add_development_dependency(%q<webmock>, [">= 0"])
120
118
  s.add_development_dependency(%q<vcr>, [">= 0"])
121
119
  s.add_development_dependency(%q<jeweler>, [">= 0"])
120
+ s.add_development_dependency(%q<debugger>, [">= 0"])
122
121
  else
123
122
  s.add_dependency(%q<httparty>, [">= 0"])
124
123
  s.add_dependency(%q<hashie>, ["~> 1"])
125
124
  s.add_dependency(%q<json>, [">= 0"])
126
125
  s.add_dependency(%q<rspec>, ["~> 2"])
127
- s.add_dependency(%q<ruby-debug>, [">= 0"])
128
- s.add_dependency(%q<ruby-debug19>, [">= 0"])
129
126
  s.add_dependency(%q<jruby-openssl>, [">= 0"])
130
127
  s.add_dependency(%q<webmock>, [">= 0"])
131
128
  s.add_dependency(%q<vcr>, [">= 0"])
132
129
  s.add_dependency(%q<jeweler>, [">= 0"])
130
+ s.add_dependency(%q<debugger>, [">= 0"])
133
131
  end
134
132
  else
135
133
  s.add_dependency(%q<httparty>, [">= 0"])
136
134
  s.add_dependency(%q<hashie>, ["~> 1"])
137
135
  s.add_dependency(%q<json>, [">= 0"])
138
136
  s.add_dependency(%q<rspec>, ["~> 2"])
139
- s.add_dependency(%q<ruby-debug>, [">= 0"])
140
- s.add_dependency(%q<ruby-debug19>, [">= 0"])
141
137
  s.add_dependency(%q<jruby-openssl>, [">= 0"])
142
138
  s.add_dependency(%q<webmock>, [">= 0"])
143
139
  s.add_dependency(%q<vcr>, [">= 0"])
144
140
  s.add_dependency(%q<jeweler>, [">= 0"])
141
+ s.add_dependency(%q<debugger>, [">= 0"])
145
142
  end
146
143
  end
147
144
 
@@ -10,26 +10,13 @@ module Harvest
10
10
  # [+active?+] true|false on whether the client is active
11
11
  # [+highrise_id+] (READONLY) the highrise id associated with this client
12
12
  # [+update_at+] (READONLY) the last modification timestamp
13
- class Client < Hashie::Dash
13
+ class Client < Hashie::Mash
14
14
  include Harvest::Model
15
-
15
+
16
16
  api_path '/clients'
17
-
18
17
 
19
- property :id
20
- property :active
21
- property :name
22
- property :details
23
- property :currency
24
- property :currency_symbol
25
- property :cache_version
26
- property :created_at
27
- property :updated_at
28
- property :highrise_id
29
- property :default_invoice_timeframe
30
- property :last_invoice_kind
31
-
32
- alias_method :active?, :active
33
- alias_method :is_active=, :active=
18
+ def is_active=(val)
19
+ self.active = val
20
+ end
34
21
  end
35
- end
22
+ end
@@ -11,21 +11,9 @@ module Harvest
11
11
  # [+phone_office+] the office phone number of the contact
12
12
  # [+phone_moble+] the moble phone number of the contact
13
13
  # [+fax+] the fax number of the contact
14
- class Contact < Hashie::Dash
14
+ class Contact < Hashie::Mash
15
15
  include Harvest::Model
16
-
16
+
17
17
  api_path '/contacts'
18
-
19
- property :id
20
- property :client_id
21
- property :email
22
- property :first_name
23
- property :last_name
24
- property :phone_office
25
- property :phone_mobile
26
- property :fax
27
- property :title
28
- property :created_at
29
- property :updated_at
30
18
  end
31
- end
19
+ end
@@ -1,24 +1,10 @@
1
1
  module Harvest
2
- class Expense < Hashie::Dash
2
+ class Expense < Hashie::Mash
3
3
  include Harvest::Model
4
4
 
5
5
  api_path '/expenses'
6
-
7
- property :id
8
- property :notes
9
- property :units
10
- property :total_cost
11
- property :project_id
12
- property :expense_category_id
13
- property :user_id
14
- property :spent_at
15
- property :created_at
16
- property :updated_at
17
- property :is_billed
18
- property :is_closed
19
- property :invoice_id
20
- property :has_receipt
21
- property :receipt_url
6
+ delegate_methods(:billed? => :is_billed,
7
+ :closed? => :is_closed)
22
8
 
23
9
  def initialize(args = {})
24
10
  args = args.stringify_keys
@@ -37,9 +23,5 @@ module Harvest
37
23
  hash[json_root].delete("receipt_url")
38
24
  end
39
25
  end
40
-
41
- alias_method :billed?, :is_billed
42
- alias_method :closed?, :is_closed
43
- alias_method :has_receipt?, :has_receipt
44
26
  end
45
27
  end
@@ -1,19 +1,10 @@
1
1
  module Harvest
2
- class ExpenseCategory < Hashie::Dash
2
+ class ExpenseCategory < Hashie::Mash
3
3
  include Harvest::Model
4
4
  api_path '/expense_categories'
5
-
6
- property :id
7
- property :name
8
- property :unit_name
9
- property :unit_price
10
- property :deactivated
11
- property :created_at
12
- property :updated_at
13
- property :cache_version
14
-
5
+
15
6
  def active?
16
7
  !deactivated
17
8
  end
18
9
  end
19
- end
10
+ end
@@ -1,46 +1,14 @@
1
1
  module Harvest
2
- class Invoice < Hashie::Dash
2
+ class Invoice < Hashie::Mash
3
3
  include Harvest::Model
4
-
4
+
5
5
  api_path '/invoices'
6
-
6
+
7
7
  attr_reader :line_items
8
-
9
- property :id
10
- property :subject
11
- property :number
12
- property :created_at
13
- property :updated_at
14
- property :issued_at
15
- property :due_at
16
- property :due_at_human_format
17
- property :due_amount
18
- property :notes
19
- property :recurring_invoice_id
20
- property :period_start
21
- property :period_end
22
- property :discount
23
- property :discount_amount
24
- property :client_key
25
- property :amount
26
- property :tax
27
- property :tax2
28
- property :tax_amount
29
- property :tax2_amount
30
- property :csv_line_items
31
- property :client_id
32
- property :estimate_id
33
- property :purchase_order
34
- property :retainer_id
35
- property :currency
36
- property :state
37
- property :kind
38
- property :import_hours
39
- property :import_expenses
40
-
8
+
41
9
  def self.json_root; "doc"; end
42
10
  # skip_json_root true
43
-
11
+
44
12
  def initialize(args = {})
45
13
  @line_items = []
46
14
  args = args.stringify_keys
@@ -48,7 +16,7 @@ module Harvest
48
16
  self.line_items = args.delete("line_items")
49
17
  super
50
18
  end
51
-
19
+
52
20
  def line_items=(raw_or_rich)
53
21
  unless raw_or_rich.nil?
54
22
  @line_items = case raw_or_rich
@@ -59,13 +27,13 @@ module Harvest
59
27
  end
60
28
  end
61
29
  end
62
-
30
+
63
31
  def as_json(*options)
64
32
  json = super(*options)
65
33
  json[json_root]["csv_line_items"] = encode_csv(@line_items)
66
34
  json
67
35
  end
68
-
36
+
69
37
  private
70
38
  def decode_csv(string)
71
39
  csv = CSV.parse(string)
@@ -73,19 +41,19 @@ module Harvest
73
41
  csv.map! {|row| headers.zip(row) }
74
42
  csv.map {|row| row.inject({}) {|h, tuple| h.update(tuple[0] => tuple[1]) } }
75
43
  end
76
-
44
+
77
45
  def encode_csv(line_items)
78
46
  if line_items.empty?
79
47
  ""
80
48
  else
81
49
  header = %w(kind description quantity unit_price amount taxed taxed2 project_id)
82
-
50
+
83
51
  # writing this in stdlib so we don't force 1.8 users to install FasterCSV and make gem dependencies wierd
84
52
  if RUBY_VERSION =~ /1.8/
85
53
  csv_data = ""
86
54
  CSV.generate_row(header, header.size, csv_data)
87
55
  line_items.each do |item|
88
- row_data = header.inject([]) {|row, attr| row << item[attr] }
56
+ row_data = header.inject([]) {|row, attr| row << item[attr] }
89
57
  CSV.generate_row(row_data, row_data.size, csv_data)
90
58
  end
91
59
  csv_data
@@ -93,11 +61,11 @@ module Harvest
93
61
  CSV.generate do |csv|
94
62
  csv << header
95
63
  line_items.each do |item|
96
- csv << header.inject([]) {|row, attr| row << item[attr] }
64
+ csv << header.inject([]) {|row, attr| row << item[attr] }
97
65
  end
98
66
  end
99
67
  end
100
68
  end
101
69
  end
102
70
  end
103
- end
71
+ end
@@ -1,18 +1,9 @@
1
1
  module Harvest
2
- class InvoiceCategory < Hashie::Dash
2
+ class InvoiceCategory < Hashie::Mash
3
3
  include Harvest::Model
4
-
4
+
5
5
  api_path '/invoice_item_categories'
6
6
  def self.json_root; "category"; end
7
-
8
- property :id
9
- property :name
10
- property :created_at
11
- property :updated_at
12
- property :use_as_expense
13
- property :use_as_service
14
-
15
- alias_method :use_as_expense?, :use_as_expense
16
- alias_method :use_as_service?, :use_as_service
7
+
17
8
  end
18
- end
9
+ end
@@ -1,12 +1,4 @@
1
1
  module Harvest
2
- class LineItem < Hashie::Dash
3
- property :kind
4
- property :description
5
- property :quantity
6
- property :unit_price
7
- property :amount
8
- property :taxed
9
- property :taxed2
10
- property :project_id
2
+ class LineItem < Hashie::Mash
11
3
  end
12
- end
4
+ end
data/lib/harvest/model.rb CHANGED
@@ -4,12 +4,12 @@ module Harvest
4
4
  base.send :include, InstanceMethods
5
5
  base.send :extend, ClassMethods
6
6
  end
7
-
7
+
8
8
  module InstanceMethods
9
9
  def to_json(*args)
10
10
  as_json(*args).to_json(*args)
11
11
  end
12
-
12
+
13
13
  def as_json(args = {})
14
14
  inner_json = self.to_hash.stringify_keys
15
15
  inner_json.delete("cache_version")
@@ -19,13 +19,13 @@ module Harvest
19
19
  { self.class.json_root => inner_json }
20
20
  end
21
21
  end
22
-
22
+
23
23
  def to_i; id; end
24
-
24
+
25
25
  def ==(other)
26
26
  id == other.id
27
27
  end
28
-
28
+
29
29
  def impersonated_user_id
30
30
  if respond_to?(:of_user) && respond_to?(:user_id)
31
31
  of_user || user_id
@@ -35,41 +35,41 @@ module Harvest
35
35
  of_user
36
36
  end
37
37
  end
38
-
38
+
39
39
  def json_root
40
40
  self.class.json_root
41
41
  end
42
42
  end
43
-
43
+
44
44
  module ClassMethods
45
45
  # This sets the API path so the API collections can use them in an agnostic way
46
46
  # @return [void]
47
47
  def api_path(path = nil)
48
48
  @_api_path ||= path
49
49
  end
50
-
50
+
51
51
  def skip_json_root(skip = nil)
52
52
  @_skip_json_root ||= skip
53
53
  end
54
-
54
+
55
55
  def skip_json_root?
56
56
  @_skip_json_root == true
57
57
  end
58
-
58
+
59
59
  def parse(json)
60
60
  parsed = String === json ? JSON.parse(json) : json
61
61
  Array.wrap(parsed).map {|attrs| skip_json_root? ? new(attrs) : new(attrs[json_root])}
62
62
  end
63
-
63
+
64
64
  def json_root
65
65
  Harvest::Model::Utility.underscore(
66
66
  Harvest::Model::Utility.demodulize(to_s)
67
67
  )
68
68
  end
69
-
69
+
70
70
  def wrap(model_or_attrs)
71
71
  case model_or_attrs
72
- when Hashie::Dash
72
+ when Hashie::Mash
73
73
  model_or_attrs
74
74
  when Hash
75
75
  new(model_or_attrs)
@@ -77,13 +77,24 @@ module Harvest
77
77
  model_or_attrs
78
78
  end
79
79
  end
80
+
81
+ def delegate_methods(options)
82
+ raise "no methods given" if options.empty?
83
+ options.each do |source, dest|
84
+ class_eval <<-EOV
85
+ def #{source}
86
+ #{dest}
87
+ end
88
+ EOV
89
+ end
90
+ end
80
91
  end
81
-
92
+
82
93
  module Utility
83
94
  class << self
84
-
95
+
85
96
  # Both methods are shamelessly ripped from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/inflections.rb
86
-
97
+
87
98
  # Removes the module part from the expression in the string.
88
99
  #
89
100
  # Examples:
@@ -92,7 +103,7 @@ module Harvest
92
103
  def demodulize(class_name_in_module)
93
104
  class_name_in_module.to_s.gsub(/^.*::/, '')
94
105
  end
95
-
106
+
96
107
  # Makes an underscored, lowercase form from the expression in the string.
97
108
  #
98
109
  # Changes '::' to '/' to convert namespaces to paths.
@@ -117,4 +128,4 @@ module Harvest
117
128
  end
118
129
  end
119
130
  end
120
- end
131
+ end
@@ -22,58 +22,21 @@ module Harvest
22
22
  # [+active_task_assignments_count+] (READONLY) the number of active task assignments
23
23
  # [+created_at+] (READONLY) when the project was created
24
24
  # [+updated_at+] (READONLY) when the project was updated
25
- class Project < Hashie::Dash
25
+ class Project < Hashie::Mash
26
26
  include Harvest::Model
27
27
 
28
28
  api_path '/projects'
29
29
 
30
- property :id
31
- property :client_id
32
- property :name
33
- property :code
34
- property :notes
35
- property :fees
36
- property :active
37
- property :billable
38
- property :budget
39
- property :budget_by
40
- property :hourly_rate
41
- property :bill_by
42
- property :created_at
43
- property :updated_at
44
- property :notify_when_over_budget
45
- property :over_budget_notification_percentage
46
- property :show_budget_to_all
47
- property :basecamp_id
48
- property :highrise_deal_id
49
- property :active_task_assignments_count
50
- property :over_budget_notified_at
51
- property :earliest_record_at
52
- property :cost_budget
53
- property :cost_budget_include_expenses
54
- property :latest_record_at
55
- property :estimate_by
56
- property :hint_earliest_record_at
57
- property :hint_latest_record_at
58
- property :active_user_assignments_count
59
- property :cache_version
60
- property :estimate
61
-
62
- alias_method :active?, :active
63
- alias_method :billable?, :billable
64
- alias_method :notify_when_over_budget?, :notify_when_over_budget
65
- alias_method :show_budget_to_all?, :show_budget_to_all
66
-
67
30
  def as_json(args = {})
68
31
  super(args).tap do |json|
69
32
  json[json_root].delete("hint_earliest_record_at")
70
33
  json[json_root].delete("hint_latest_record_at")
71
34
  end
72
35
  end
73
-
36
+
74
37
  def self.parse(json)
75
38
  json = String === json ? JSON.parse(json) : json
76
- Array.wrap(json).each do |attrs|
39
+ Array.wrap(json).each do |attrs|
77
40
  # need to cleanup some attributes
78
41
  project_attrs = attrs[json_root] || {}
79
42
  project_attrs["hint_latest_record_at"] = project_attrs.delete("hint-latest-record-at")
@@ -82,4 +45,4 @@ module Harvest
82
45
  super(json)
83
46
  end
84
47
  end
85
- end
48
+ end
@@ -1,5 +1,5 @@
1
1
  module Harvest
2
-
2
+
3
3
  # The model that contains the information about the user's rate limit
4
4
  #
5
5
  # == Fields
@@ -8,17 +8,11 @@ module Harvest
8
8
  # [+timeframe_limit+] The amount of seconds before a rate limit refresh occurs
9
9
  # [+max_calls+] The number of requests you can make within the +timeframe_limit+
10
10
  # [+lockout_seconds+] If you exceed the rate limit, how long you will be locked out from Harvest
11
- class RateLimitStatus < Hashie::Dash
11
+ class RateLimitStatus < Hashie::Mash
12
12
  include Harvest::Model
13
-
13
+
14
14
  skip_json_root true
15
-
16
- property :last_access_at
17
- property :count
18
- property :timeframe_limit
19
- property :max_calls
20
- property :lockout_seconds
21
-
15
+
22
16
  # Returns true if the user is over their rate limit
23
17
  # @return [Boolean]
24
18
  # @see http://www.getharvest.com/api
@@ -26,4 +20,4 @@ module Harvest
26
20
  count > max_calls
27
21
  end
28
22
  end
29
- end
23
+ end
data/lib/harvest/task.rb CHANGED
@@ -9,25 +9,14 @@ module Harvest
9
9
  # [+deactivated+] whether the task is deactivated
10
10
  # [+hourly_rate+] what the default hourly rate for the task is
11
11
  # [+default?+] whether to add this task to new projects by default
12
- class Task < Hashie::Dash
12
+ class Task < Hashie::Mash
13
13
  include Harvest::Model
14
14
 
15
15
  api_path '/tasks'
16
+ delegate_methods :default? => :is_default
16
17
 
17
- property :id
18
- property :name
19
- property :billable_by_default
20
- property :deactivated
21
- property :default_hourly_rate
22
- property :is_default
23
- property :created_at
24
- property :updated_at
25
- property :cache_version
26
-
27
18
  def active?
28
19
  !deactivated
29
20
  end
30
-
31
- alias_method :default?, :is_default
32
21
  end
33
- end
22
+ end
@@ -1,25 +1,14 @@
1
1
  module Harvest
2
- class TaskAssignment < Hashie::Dash
2
+ class TaskAssignment < Hashie::Mash
3
3
  include Harvest::Model
4
4
 
5
- property :id
6
- property :task_id
7
- property :project_id
8
- property :billable
9
- property :deactivated
10
- property :hourly_rate
11
- property :budget
12
- property :estimate
13
- property :created_at
14
- property :updated_at
15
-
16
5
  def initialize(args = {})
17
6
  args = args.stringify_keys
18
7
  self.task = args.delete("task") if args["task"]
19
8
  self.project = args.delete("project") if args["project"]
20
9
  super
21
10
  end
22
-
11
+
23
12
  def task=(task)
24
13
  self["task_id"] = task.to_i
25
14
  end
@@ -35,7 +24,5 @@ module Harvest
35
24
  def task_as_json
36
25
  {"task" => {"id" => task_id}}
37
26
  end
38
-
39
- alias_method :billable?, :billable
40
27
  end
41
- end
28
+ end
@@ -1,46 +1,25 @@
1
1
  module Harvest
2
- class TimeEntry < Hashie::Dash
2
+ class TimeEntry < Hashie::Mash
3
3
  include Harvest::Model
4
-
5
- property :id
6
- property :client
7
- property :hours
8
- property :notes
9
-
10
- property :project_id
11
- property :task_id
12
- property :project
13
- property :task
14
- property :spent_at
15
- property :created_at
16
- property :updated_at
17
- property :user_id
18
- property :of_user
19
- property :is_closed
20
- property :is_billed
21
- property :timer_started_at
22
- property :adjustment_record
23
- property :hours_without_timer
24
-
4
+
25
5
  skip_json_root true
26
-
6
+ delegate_methods(:closed? => :is_closed,
7
+ :billed? => :is_billed)
8
+
27
9
  def initialize(args = {})
28
10
  args = args.stringify_keys
29
11
  self.spent_at = args.delete("spent_at") if args["spent_at"]
30
12
  super
31
13
  end
32
-
14
+
33
15
  def spent_at=(date)
34
16
  self["spent_at"] = (String === date ? Time.parse(date) : date)
35
17
  end
36
-
18
+
37
19
  def as_json(args = {})
38
- super(args).stringify_keys.tap do |hash|
20
+ super(args).stringify_keys.tap do |hash|
39
21
  hash.update("spent_at" => (spent_at.nil? ? nil : spent_at.to_time.xmlschema))
40
22
  end
41
23
  end
42
-
43
- alias_method :closed?, :is_closed
44
- alias_method :billed?, :is_billed
45
24
  end
46
25
  end
data/lib/harvest/user.rb CHANGED
@@ -15,51 +15,14 @@ module Harvest
15
15
  # [+contractor?+] whether the user is a contractor
16
16
  # [+contractor?+] whether the user is a contractor
17
17
  # [+timezone+] the timezone for the user.
18
- class User < Hashie::Dash
18
+ class User < Hashie::Mash
19
19
  include Harvest::Model
20
20
 
21
21
  api_path '/people'
22
- property :id
23
- property :email
24
- property :first_name
25
- property :last_name
26
- property :has_access_to_all_future_projects
27
- property :default_hourly_rate
28
- property :is_active
29
- property :is_admin
30
- property :is_contractor
31
- property :telephone
32
- property :department
33
- property :timezone
34
- property :password
35
- property :first_timer
36
- property :wants_newsletter
37
- property :preferred_project_status_reports_screen
38
- property :preferred_approval_screen
39
- property :created_at
40
- property :updated_at
41
- property :twitter_username
42
- property :preferred_entry_method
43
- property :default_time_project_id
44
- property :default_task_id
45
- property :default_expense_category_id
46
- property :opensocial_identifier
47
- property :duplicate_timesheet_wants_notes
48
- property :wants_timesheet_duplication
49
- property :cache_version
50
- property :email_after_submit
51
- property :default_expense_project_id
52
- property :identity_url
53
- property :timestamp_timers
54
- property :weekly_digest_sent_on
55
- property :wants_weekly_digest
56
- property :password_change_required
57
- property :has_timesheet_2012_beta
58
- property :timesheet_2012_beta_control_group
59
22
 
60
- alias_method :active?, :is_active
61
- alias_method :admin?, :is_admin
62
- alias_method :contractor?, :is_contractor
23
+ delegate_methods(:active? => :is_active,
24
+ :admin? => :is_admin,
25
+ :contractor? => :is_contractor)
63
26
 
64
27
  def initialize(args = {})
65
28
  args = args.stringify_keys
@@ -1,41 +1,30 @@
1
1
  module Harvest
2
- class UserAssignment < Hashie::Dash
2
+ class UserAssignment < Hashie::Mash
3
3
  include Harvest::Model
4
-
5
- property :id
6
- property :user_id
7
- property :project_id
8
- property :deactivated
9
- property :is_project_manager
10
- property :hourly_rate
11
- property :created_at
12
- property :updated_at
13
- property :budget
14
- property :estimate
15
-
4
+
5
+ delegate_methods :project_manager? => :is_project_manager
6
+
16
7
  def initialize(args = {})
17
8
  args = args.stringify_keys
18
9
  self.user = args.delete("user") if args["user"]
19
10
  self.project = args.delete("project") if args["project"]
20
11
  super
21
12
  end
22
-
13
+
23
14
  def user=(user)
24
15
  self["user_id"] = user.to_i
25
16
  end
26
-
17
+
27
18
  def project=(project)
28
19
  self["project_id"] = project.to_i
29
20
  end
30
-
21
+
31
22
  def active?
32
23
  !deactivated
33
24
  end
34
-
25
+
35
26
  def user_as_json
36
27
  {"user" => {"id" => user_id}}
37
28
  end
38
-
39
- alias_method :project_manager?, :is_project_manager
40
29
  end
41
- end
30
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'harvest reporting' do
3
+ describe 'harvest reporting' do
4
4
  it 'allows project and people entry reporting' do
5
5
  cassette("reports1") do
6
6
  user = harvest.users.create(
@@ -20,8 +20,8 @@ describe 'harvest reporting' do
20
20
  harvest.user_assignments.create("project" => project1, "user" => user)
21
21
  harvest.user_assignments.create("project" => project2, "user" => user)
22
22
 
23
- entry1 = harvest.time.create("notes" => "billable entry1", "hours" => 3, "spent_at" => "12/28/2009", "task_id" => task1.id, "project_id" => project1.id, "of_user" => user.id)
24
- entry2 = harvest.time.create("notes" => "non billable entry2", "hours" => 6, "spent_at" => "12/28/2009", "task_id" => task2.id, "project_id" => project2.id, "of_user" => user.id)
23
+ entry1 = harvest.time.create("notes" => "billable entry1", "hours" => 3, "spent_at" => "2009/12/28", "task_id" => task1.id, "project_id" => project1.id, "of_user" => user.id)
24
+ entry2 = harvest.time.create("notes" => "non billable entry2", "hours" => 6, "spent_at" => "2009/12/28", "task_id" => task2.id, "project_id" => project2.id, "of_user" => user.id)
25
25
 
26
26
  harvest.reports.time_by_project(project1, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).first.should == entry1
27
27
 
@@ -40,7 +40,7 @@ describe 'harvest reporting' do
40
40
 
41
41
  harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :billable => true).first.should == entry1
42
42
  harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :billable => false).first.should == entry2
43
-
43
+
44
44
  client2 = harvest.clients.create("name" => "Phil's Sandwich Shop")
45
45
  harvest.reports.projects_by_client(client).map(&:id).to_set.should == [project1, project2].map(&:id).to_set
46
46
  harvest.reports.projects_by_client(client2).should == []
@@ -69,9 +69,9 @@ describe 'harvest reporting' do
69
69
  "project_id" => project.id,
70
70
  "user_id" => user.id
71
71
  )
72
-
72
+
73
73
  harvest.reports.expenses_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).first.should == expense
74
-
74
+
75
75
  my_user = harvest.users.all.detect {|u| u.email == credentials["username"]}
76
76
  my_user.should_not be_nil
77
77
  harvest.reports.expenses_by_user(my_user, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).should == []
@@ -7,23 +7,23 @@ describe 'harvest time tracking' do
7
7
  project = harvest.projects.create("name" => "Tracking Project", "client_id" => client.id)
8
8
  harvest.projects.create_task(project, "A billable task")
9
9
  task = harvest.tasks.all.detect {|t| t.name == "A billable task"}
10
-
11
- entry = harvest.time.create("notes" => "Test api support", "hours" => 3, "spent_at" => "12/28/2009", "task_id" => task.id, "project_id" => project.id)
10
+
11
+ entry = harvest.time.create("notes" => "Test api support", "hours" => 3, "spent_at" => "2009/12/28", "task_id" => task.id, "project_id" => project.id)
12
12
  entry.notes.should == "Test api support"
13
-
13
+
14
14
  entry.notes = "Upgraded to JSON"
15
15
  entry = harvest.time.update(entry)
16
16
  entry.notes.should == "Upgraded to JSON"
17
-
17
+
18
18
  harvest.time.delete(entry)
19
19
  harvest.time.all(Time.utc(2009, 12, 28)).should == []
20
20
  end
21
21
  end
22
-
22
+
23
23
  it 'allows you to save entries for a different user' do
24
24
  cassette("time_tracking2") do
25
25
  user = harvest.users.create(
26
- "email" => "frank@example.com",
26
+ "email" => "frank@example.com",
27
27
  "first_name" => "Frank",
28
28
  "last_name" => "Doe",
29
29
  "password" => "secure"
@@ -33,21 +33,21 @@ describe 'harvest time tracking' do
33
33
  harvest.user_assignments.create("project" => project, "user" => user)
34
34
  harvest.projects.create_task(project, "A billable task for tuxes")
35
35
  task = harvest.tasks.all.detect {|t| t.name == "A billable task for tuxes"}
36
-
37
- entry = harvest.time.create("notes" => "Test api support", "hours" => 3, "spent_at" => "12/28/2009", "task_id" => task.id, "project_id" => project.id, "of_user" => user.id)
36
+
37
+ entry = harvest.time.create("notes" => "Test api support", "hours" => 3, "spent_at" => "2009/12/28", "task_id" => task.id, "project_id" => project.id, "of_user" => user.id)
38
38
  harvest.time.all(Time.utc(2009, 12, 28), user).should == [entry]
39
-
39
+
40
40
  entry.notes = "Updating notes"
41
41
  entry = harvest.time.update(entry, user)
42
42
  entry.notes.should == "Updating notes"
43
-
43
+
44
44
  entry = harvest.time.find(entry, user)
45
45
  entry.notes.should == "Updating notes"
46
-
46
+
47
47
  harvest.time.delete(entry, user)
48
48
  harvest.time.all(Time.utc(2009, 12, 28), user).should == []
49
49
  end
50
50
  end
51
-
51
+
52
52
  it 'allows toggling of timers'
53
- end
53
+ end
@@ -7,60 +7,61 @@ describe 'harvest users' do
7
7
  "first_name" => "Edgar",
8
8
  "last_name" => "Ruth",
9
9
  "email" => "edgar@ruth.com",
10
- "password" => "mypassword",
11
10
  "timezone" => "cst",
12
11
  "is_admin" => "false",
13
12
  "telephone" => "444-4444"
14
13
  )
15
- user.id.should_not be_blank
16
-
14
+ user.id.should_not be_nil
15
+
17
16
  user.first_name = "Joey"
18
17
  user = harvest.users.update(user)
19
18
  user.first_name.should == "Joey"
20
-
19
+
21
20
  id = harvest.users.delete(user)
22
21
  harvest.users.all.map(&:id).should_not include(id)
23
22
  end
24
23
  end
25
-
24
+
26
25
  it "allows activating and deactivating users" do
27
26
  cassette("users2") do
28
27
  user = harvest.users.create(
29
28
  "first_name" => "John",
30
29
  "last_name" => "Ruth",
31
30
  "email" => "john@ruth.com",
32
- "password" => "mypassword",
33
31
  "timezone" => "cst",
34
32
  "is_admin" => "false",
35
33
  "telephone" => "444-4444"
36
34
  )
37
35
  user.should be_active
38
-
36
+
39
37
  user = harvest.users.deactivate(user)
40
38
  user.should_not be_active
41
-
39
+
42
40
  user = harvest.users.activate(user)
43
41
  user.should be_active
42
+
43
+ harvest.users.delete(user)
44
44
  end
45
45
  end
46
-
46
+
47
47
  it "allows password resets" do
48
48
  cassette("users3") do
49
+ pending "Something is wrong with resetting passwords"
49
50
  user = harvest.users.create(
50
51
  "first_name" => "Timmy",
51
52
  "last_name" => "Ruth",
52
53
  "email" => "timmy@ruth.com",
53
- "password" => "mypassword",
54
54
  "timezone" => "cst",
55
55
  "is_admin" => "false",
56
56
  "telephone" => "444-4444"
57
57
  )
58
58
  user.should be_active
59
-
59
+
60
60
  harvest.users.reset_password(user) # nothing else to assert
61
+ harvest.users.delete(user)
61
62
  end
62
63
  end
63
-
64
+
64
65
  context "assignments" do
65
66
  it "allows adding, updating, and removing users from projects" do
66
67
  cassette('users4') do
@@ -85,10 +86,10 @@ describe 'harvest users' do
85
86
  "is_admin" => "false",
86
87
  "telephone" => "444-4444"
87
88
  )
88
-
89
+
89
90
 
90
91
  assignment = harvest.user_assignments.create("project" => project, "user" => user)
91
-
92
+
92
93
  assignment.hourly_rate = 100
93
94
  assignment = harvest.user_assignments.update(assignment)
94
95
  assignment.hourly_rate.should == "100.0"
@@ -99,4 +100,4 @@ describe 'harvest users' do
99
100
  end
100
101
  end
101
102
  end
102
- end
103
+ end
data/spec/spec_helper.rb CHANGED
@@ -4,36 +4,28 @@ require 'vcr'
4
4
 
5
5
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require File.expand_path(f) }
6
6
 
7
- VCR.config do |c|
7
+ VCR.configure do |c|
8
8
  c.cassette_library_dir = '.cassettes'
9
- c.stub_with :webmock
9
+ c.hook_into :webmock
10
10
  end
11
11
 
12
+ FileUtils.rm(Dir["#{VCR.configuration.cassette_library_dir}/*"]) if ENV['VCR_REFRESH'] == 'true'
13
+
12
14
  RSpec.configure do |config|
13
15
  config.include HarvestedHelpers
14
-
16
+
15
17
  config.before(:suite) do
16
18
  WebMock.allow_net_connect!
17
19
  cassette("clean") do
18
20
  HarvestedHelpers.clean_remote
19
21
  end
20
22
  end
21
-
23
+
22
24
  config.before(:each) do
23
25
  WebMock.allow_net_connect!
24
26
  end
25
-
27
+
26
28
  def cassette(*args)
27
- if ENV['CACHE'] == "false"
28
- if args.last.is_a?(Hash)
29
- last = args.pop
30
- last[:record] = :all
31
- args << last
32
- else
33
- args << {:record => :all}
34
- end
35
- end
36
-
37
29
  VCR.use_cassette(*args) do
38
30
  yield
39
31
  end
@@ -4,33 +4,33 @@ module HarvestedHelpers
4
4
  @credentials ||= YAML.load_file("#{File.dirname(__FILE__)}/harvest_credentials.yml")
5
5
  end
6
6
  def credentials; HarvestedHelpers.credentials; end
7
-
7
+
8
8
  def self.simple_harvest
9
9
  Harvest.client(credentials["subdomain"], credentials["username"], credentials["password"], :ssl => true)
10
10
  end
11
-
11
+
12
12
  # def connect_to_harvest
13
13
  # @harvest = Harvest.hardy_client(credentials["subdomain"], credentials["username"], credentials["password"], :ssl => true)
14
14
  # end
15
-
15
+
16
16
  # def harvest; @harvest; end
17
17
  def harvest; @harvest ||= HarvestedHelpers.simple_harvest; end
18
-
18
+
19
19
  def hardy_harvest
20
20
  Harvest.hardy_client(credentials["subdomain"], credentials["username"], credentials["password"], :ssl => true)
21
21
  end
22
-
22
+
23
23
  def self.clean_remote
24
24
  harvest = simple_harvest
25
25
  harvest.users.all.each do |u|
26
- harvest.reports.expenses_by_user(u, Time.utc(2000, 1, 1), Time.utc(2011, 6,21)).each do |expense|
26
+ harvest.reports.expenses_by_user(u, Time.utc(2000, 1, 1), Time.utc(2013, 6,21)).each do |expense|
27
27
  harvest.expenses.delete(expense, u)
28
28
  end
29
-
30
- harvest.reports.time_by_user(u, Time.utc(2000, 1, 1), Time.utc(2011, 6,21)).each do |time|
29
+
30
+ harvest.reports.time_by_user(u, Time.utc(2000, 1, 1), Time.utc(2013, 6,21)).each do |time|
31
31
  harvest.time.delete(time, u)
32
32
  end
33
-
33
+
34
34
  harvest.users.delete(u) if u.email != credentials["username"]
35
35
  end
36
36
 
@@ -41,4 +41,4 @@ module HarvestedHelpers
41
41
  harvest.send(collection).all.each {|m| harvest.send(collection).delete(m) }
42
42
  end
43
43
  end
44
- end
44
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harvested
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-21 00:00:00.000000000 Z
12
+ date: 2012-08-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httparty
@@ -76,23 +76,7 @@ dependencies:
76
76
  - !ruby/object:Gem::Version
77
77
  version: '2'
78
78
  - !ruby/object:Gem::Dependency
79
- name: ruby-debug
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- type: :development
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: '0'
94
- - !ruby/object:Gem::Dependency
95
- name: ruby-debug19
79
+ name: jruby-openssl
96
80
  requirement: !ruby/object:Gem::Requirement
97
81
  none: false
98
82
  requirements:
@@ -108,7 +92,7 @@ dependencies:
108
92
  - !ruby/object:Gem::Version
109
93
  version: '0'
110
94
  - !ruby/object:Gem::Dependency
111
- name: jruby-openssl
95
+ name: webmock
112
96
  requirement: !ruby/object:Gem::Requirement
113
97
  none: false
114
98
  requirements:
@@ -124,7 +108,7 @@ dependencies:
124
108
  - !ruby/object:Gem::Version
125
109
  version: '0'
126
110
  - !ruby/object:Gem::Dependency
127
- name: webmock
111
+ name: vcr
128
112
  requirement: !ruby/object:Gem::Requirement
129
113
  none: false
130
114
  requirements:
@@ -140,7 +124,7 @@ dependencies:
140
124
  - !ruby/object:Gem::Version
141
125
  version: '0'
142
126
  - !ruby/object:Gem::Dependency
143
- name: vcr
127
+ name: jeweler
144
128
  requirement: !ruby/object:Gem::Requirement
145
129
  none: false
146
130
  requirements:
@@ -156,7 +140,7 @@ dependencies:
156
140
  - !ruby/object:Gem::Version
157
141
  version: '0'
158
142
  - !ruby/object:Gem::Dependency
159
- name: jeweler
143
+ name: debugger
160
144
  requirement: !ruby/object:Gem::Requirement
161
145
  none: false
162
146
  requirements:
@@ -277,7 +261,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
277
261
  version: '0'
278
262
  segments:
279
263
  - 0
280
- hash: -914741534374254830
264
+ hash: -3859920276334034236
281
265
  required_rubygems_version: !ruby/object:Gem::Requirement
282
266
  none: false
283
267
  requirements: