harvested 0.5.3 → 0.6.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.
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: