fitgem 0.3.6 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rvmrc +1 -1
- data/.travis.yml +3 -0
- data/.yardopts +7 -0
- data/README.md +17 -79
- data/Rakefile +11 -0
- data/changelog.md +65 -0
- data/fitgem.gemspec +17 -3
- data/lib/fitgem.rb +3 -1
- data/lib/fitgem/activities.rb +89 -18
- data/lib/fitgem/body_measurements.rb +60 -13
- data/lib/fitgem/client.rb +145 -30
- data/lib/fitgem/devices.rb +15 -5
- data/lib/fitgem/errors.rb +7 -4
- data/lib/fitgem/food_form.rb +18 -0
- data/lib/fitgem/foods.rb +89 -23
- data/lib/fitgem/friends.rb +39 -8
- data/lib/fitgem/helpers.rb +19 -5
- data/lib/fitgem/notifications.rb +115 -36
- data/lib/fitgem/sleep.rb +40 -2
- data/lib/fitgem/time_range.rb +45 -29
- data/lib/fitgem/units.rb +39 -2
- data/lib/fitgem/users.rb +23 -11
- data/lib/fitgem/version.rb +1 -1
- data/lib/fitgem/water.rb +30 -14
- data/spec/fitgem_constructor_spec.rb +19 -0
- data/spec/fitgem_helper_spec.rb +78 -0
- data/spec/fitgem_notifications_spec.rb +173 -0
- data/spec/fitgem_spec.rb +5 -52
- metadata +114 -78
data/lib/fitgem/users.rb
CHANGED
@@ -1,18 +1,30 @@
|
|
1
1
|
module Fitgem
|
2
2
|
class Client
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# Get information aobut current user
|
4
|
+
#
|
5
|
+
# @param [Hash] opts User information request data
|
6
|
+
# @return [Hash] User information
|
7
|
+
def user_info(opts={})
|
8
|
+
get("/user/#{@user_id}/profile.json", opts)
|
6
9
|
end
|
7
10
|
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
11
|
+
# Update profile information for current user
|
12
|
+
#
|
13
|
+
# @param [Hash] opts User profile information
|
14
|
+
# @option opts [String] :gender Gender, valid values are MALE, FEMALE, NA
|
15
|
+
# @option opts [DateTime, Date, String] :birthday Birthday, in
|
16
|
+
# "yyyy-MM-dd" if a String
|
17
|
+
# @option opts [Decimal, Integer, String] :height Height, in format
|
18
|
+
# "X.XX" if a string
|
19
|
+
# @option opts [String] :nickname Nickname
|
20
|
+
# @option opts [String] :fullName Full name
|
21
|
+
# @option opts [String] :timezone Time zone; in the format
|
22
|
+
# "America/Los Angelos"
|
23
|
+
#
|
24
|
+
# @return [Hash] Hash containing updated profile information
|
25
|
+
def update_user_info(opts)
|
26
|
+
opts[:birthday] = format_date(opts[:birthday]) if opts[:birthday]
|
27
|
+
post("/user/#{@user_id}/profile.json", opts)
|
16
28
|
end
|
17
29
|
|
18
30
|
end
|
data/lib/fitgem/version.rb
CHANGED
data/lib/fitgem/water.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
module Fitgem
|
2
2
|
class Client
|
3
|
-
|
4
3
|
# ==========================================
|
5
4
|
# Water Retrieval Methods
|
6
5
|
# ==========================================
|
6
|
+
|
7
|
+
# Get water log entries for the supplied date
|
8
|
+
#
|
9
|
+
# @param [DateTime, Date, String] date
|
10
|
+
# @return [Hash] Hash containing the summary of the days logs, and a
|
11
|
+
# list of all individual entries
|
7
12
|
def water_on_date(date)
|
8
13
|
get("/user/#{@user_id}/foods/log/water/date/#{format_date(date)}.json")
|
9
14
|
end
|
@@ -13,22 +18,33 @@ module Fitgem
|
|
13
18
|
# Water Logging Methods
|
14
19
|
# ==========================================
|
15
20
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
# Log water consumption to fitbit
|
22
|
+
#
|
23
|
+
# @param [Hash] opts Water consumption data
|
24
|
+
# @option opts [Integer, Decimal, String] :amount Amount of water
|
25
|
+
# consumed; if String must be in "X.X" format
|
26
|
+
# @option opts [String] :unit Unit of water consumed; valid values
|
27
|
+
# are ("ml", "fl oz" or "cup")
|
28
|
+
# @option opts [DateTime, Date, String] :date Date of water
|
29
|
+
# consumption
|
30
|
+
#
|
31
|
+
# @return [Hash] Summary of logged information
|
32
|
+
def log_water(opts)
|
33
|
+
unless opts[:amount] && opts[:date]
|
34
|
+
raise Fitgem::InvalidArgumentError, "Must include both an :amount and :date to log water"
|
22
35
|
end
|
23
|
-
post("/user/#{@user_id}/foods/log/water.json",options)
|
24
|
-
end
|
25
36
|
|
26
|
-
|
27
|
-
|
28
|
-
|
37
|
+
opts[:date] = format_date(opts[:date])
|
38
|
+
post("/user/#{@user_id}/foods/log/water.json",opts)
|
39
|
+
end
|
29
40
|
|
30
|
-
|
31
|
-
|
41
|
+
# Delete logged water consumption
|
42
|
+
#
|
43
|
+
# @param [Integer, String] water_log_id The id of previously logged
|
44
|
+
# water consumption
|
45
|
+
# @return [Hash] Empty hash denotes success
|
46
|
+
def delete_water_log(water_log_id)
|
47
|
+
delete("/user/-/foods/log/water/#{water_log_id}.json")
|
32
48
|
end
|
33
49
|
end
|
34
50
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fitgem::Client do
|
4
|
+
describe "constructor" do
|
5
|
+
it "requires consumer_key" do
|
6
|
+
expect {
|
7
|
+
opts = { :consumer_secret => "12345" }
|
8
|
+
client = Fitgem::Client.new(opts)
|
9
|
+
}.to raise_error(Fitgem::InvalidArgumentError, "Missing required options: consumer_key")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "requires consumer_secret" do
|
13
|
+
expect {
|
14
|
+
opts = { :consumer_key => "12345" }
|
15
|
+
client = Fitgem::Client.new(opts)
|
16
|
+
}.to raise_error(Fitgem::InvalidArgumentError, "Missing required options: consumer_secret")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fitgem::Client do
|
4
|
+
before(:each) do
|
5
|
+
@client = Fitgem::Client.new({
|
6
|
+
:consumer_key => '12345',
|
7
|
+
:consumer_secret => '67890'
|
8
|
+
})
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#construct_date_range_fragment" do
|
12
|
+
it 'should format the correct URI fragment based on a base date and end date' do
|
13
|
+
frag = @client.construct_date_range_fragment({:base_date => '2011-03-07', :end_date => '2011-03-14'})
|
14
|
+
frag.should == 'date/2011-03-07/2011-03-14'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should format the correct URI fragment based on a base date and period' do
|
18
|
+
frag = @client.construct_date_range_fragment({:base_date => '2011-03-07', :period => '7d'})
|
19
|
+
frag.should == 'date/2011-03-07/7d'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should raise an error unless there is a base date AND either a period or an end date' do
|
23
|
+
expect {
|
24
|
+
@client.construct_date_range_fragment({:base_date => '2011-03-07'})
|
25
|
+
}.to raise_error(Fitgem::InvalidTimeRange)
|
26
|
+
|
27
|
+
expect {
|
28
|
+
@client.construct_date_range_fragment({:period => '1y'})
|
29
|
+
}.to raise_error(Fitgem::InvalidTimeRange)
|
30
|
+
|
31
|
+
expect {
|
32
|
+
@client.construct_date_range_fragment({:end_date => '2011-03-07', :period => '7d'})
|
33
|
+
}.to raise_error(Fitgem::InvalidTimeRange)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#format_date" do
|
38
|
+
it "accepts DateTime objects" do
|
39
|
+
date = DateTime.strptime('2011-03-19','%Y-%m-%d')
|
40
|
+
@client.format_date(date).should == '2011-03-19'
|
41
|
+
end
|
42
|
+
|
43
|
+
it "accepts strings in YYYY-MM-DD format" do
|
44
|
+
@client.format_date('2011-03-19').should == '2011-03-19'
|
45
|
+
end
|
46
|
+
|
47
|
+
it "accepts the string 'today' to denote the current date" do
|
48
|
+
today = Date.today.strftime("%Y-%m-%d")
|
49
|
+
@client.format_date('today').should == today
|
50
|
+
end
|
51
|
+
|
52
|
+
it "accepts the string 'yesterday' to denote the day previous to today" do
|
53
|
+
yesterday = (Date.today-1).strftime("%Y-%m-%d")
|
54
|
+
@client.format_date('yesterday').should == yesterday
|
55
|
+
end
|
56
|
+
|
57
|
+
it "rejects strings that are not in YYY-MM-DD format" do
|
58
|
+
date = "2011-3-2"
|
59
|
+
expect {
|
60
|
+
@client.format_date(date)
|
61
|
+
}.to raise_error Fitgem::InvalidDateArgument, "Invalid date (2011-3-2), must be in yyyy-MM-dd format"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "rejects strings are formatted correctly but include non-numeric elements" do
|
65
|
+
date = "2011-aa-20"
|
66
|
+
expect {
|
67
|
+
@client.format_date(date)
|
68
|
+
}.to raise_error Fitgem::InvalidDateArgument, "Invalid date (2011-aa-20), must be in yyyy-MM-dd format"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "rejects arguments that are not a Date, Time, DateTime, or String" do
|
72
|
+
date = 200
|
73
|
+
expect {
|
74
|
+
@client.format_date(date)
|
75
|
+
}.to raise_error Fitgem::InvalidDateArgument, "Date used must be a date/time object or a string in the format YYYY-MM-DD; supplied argument is a Fixnum"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe Fitgem::Client do
|
5
|
+
before(:each) do
|
6
|
+
@client = Fitgem::Client.new({:consumer_key => '12345', :consumer_secret => '56789'})
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#subscriptions" do
|
10
|
+
before(:each) do
|
11
|
+
@client.stub :get
|
12
|
+
end
|
13
|
+
|
14
|
+
it "calls #make_headers to create the headers for the API call" do
|
15
|
+
opts = { :subscriber_id => "5555", :type => :all }
|
16
|
+
@client.should_receive(:make_headers).with({:type=>:all, :subscriber_id=>"5555"})
|
17
|
+
@client.subscriptions(opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "calls #get with the correct url and headers" do
|
21
|
+
opts = { :subscriber_id => "5555", :type => :all }
|
22
|
+
@client.should_receive(:get).with("/user/-/apiSubscriptions.json", {"X-Fitbit-Subscriber-Id"=>"5555"})
|
23
|
+
@client.subscriptions(opts)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#create_subscription" do
|
28
|
+
before(:each) do
|
29
|
+
@resp = OpenStruct.new
|
30
|
+
@client.stub(:raw_post).and_return(@resp)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "adds the :use_subscription_id flag and calls #make_headers" do
|
34
|
+
opts = { :subscriber_id => "5555", :type => :all, :subscription_id => "320" }
|
35
|
+
@client.should_receive(:make_headers).with({ :subscriber_id => "5555", :type => :all, :subscription_id => "320", :use_subscription_id => true })
|
36
|
+
@client.create_subscription(opts)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "calls #raw_post with the correct url and headers for :all collection type" do
|
40
|
+
opts = { :subscriber_id => "5555", :type => :all, :subscription_id => "320", :use_subscription_id => true }
|
41
|
+
@client.should_receive(:raw_post).once.with("/user/-/apiSubscriptions/320.json", "", {"X-Fitbit-Subscriber-Id"=>"5555"})
|
42
|
+
@client.create_subscription(opts)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "calls #raw_post with the correct url and headers for :sleep collection type" do
|
46
|
+
opts = { :subscriber_id => "5555", :type => :sleep, :subscription_id => "320", :use_subscription_id => true }
|
47
|
+
@client.should_receive(:raw_post).once.with("/user/-/sleep/apiSubscriptions/320.json", "", {"X-Fitbit-Subscriber-Id"=>"5555"})
|
48
|
+
@client.create_subscription(opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "calls #extract_response_body to get the JSON body" do
|
52
|
+
opts = { :subscriber_id => "5555", :type => :all, :subscription_id => "320", :use_subscription_id => true }
|
53
|
+
@client.should_receive(:extract_response_body)
|
54
|
+
@client.create_subscription(opts)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns the code and the JSON body in an array" do
|
58
|
+
opts = { :subscriber_id => "5555", :type => :all, :subscription_id => "320", :use_subscription_id => true }
|
59
|
+
@resp.should_receive(:code)
|
60
|
+
@client.create_subscription(opts).should be_a(Array)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#remove_subscription" do
|
65
|
+
before(:each) do
|
66
|
+
@resp = OpenStruct.new
|
67
|
+
@client.stub(:raw_delete).and_return(@resp)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "adds the :use_subscription_id flag and calls #make_headers" do
|
71
|
+
opts = { :subscriber_id => "5555", :type => :all, :subscription_id => "320" }
|
72
|
+
@client.should_receive(:make_headers).with({ :subscriber_id => "5555", :type => :all, :subscription_id => "320", :use_subscription_id => true })
|
73
|
+
@client.remove_subscription(opts)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "calls #raw_delete with the correct url and headers for :all collection type" do
|
77
|
+
opts = { :subscriber_id => "5555", :type => :all, :subscription_id => "320", :use_subscription_id => true }
|
78
|
+
@client.should_receive(:raw_delete).once.with("/user/-/apiSubscriptions/320.json", {"X-Fitbit-Subscriber-Id"=>"5555"})
|
79
|
+
@client.remove_subscription(opts)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "calls #extract_response_body to get the JSON body" do
|
83
|
+
opts = { :subscriber_id => "5555", :type => :all, :subscription_id => "320", :use_subscription_id => true }
|
84
|
+
@client.should_receive(:extract_response_body)
|
85
|
+
@client.remove_subscription(opts)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "returns the code and the JSON body in an array" do
|
89
|
+
opts = { :subscriber_id => "5555", :type => :all, :subscription_id => "320", :use_subscription_id => true }
|
90
|
+
@resp.should_receive(:code)
|
91
|
+
@client.remove_subscription(opts).should be_a(Array)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#validate_subscription_types" do
|
96
|
+
it "raises an exception if an invalid type is passed in" do
|
97
|
+
expect {
|
98
|
+
@client.send(:validate_subscription_type, :every_single_thing)
|
99
|
+
}.to raise_error Fitgem::InvalidArgumentError, "Invalid subscription type (valid values are sleep, body, activities, foods, all)"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "raises an exception if no type is supplied" do
|
103
|
+
opts = { :opt1 => 'hello!' }
|
104
|
+
expect {
|
105
|
+
@client.send(:validate_subscription_type, opts[:type])
|
106
|
+
}.to raise_error Fitgem::InvalidArgumentError
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "#make_headers" do
|
111
|
+
it "adds the subscriber id header" do
|
112
|
+
opts = { :subscriber_id => '5555', :subscription_id => '320-activity' }
|
113
|
+
headers = @client.send(:make_headers, opts)
|
114
|
+
headers.size.should == 1
|
115
|
+
headers['X-Fitbit-Subscriber-Id'].should == "5555"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#make_subscription_url" do
|
120
|
+
it "creates the correct URL when no specific subscription id is used" do
|
121
|
+
opts = { :subscription_id => "320", :type => :all }
|
122
|
+
@client.send(:make_subscription_url, opts).should == "/user/-/apiSubscriptions.json"
|
123
|
+
end
|
124
|
+
|
125
|
+
it "creates the correct URL for :all collection types" do
|
126
|
+
opts = { :subscription_id => "320", :type => :all, :use_subscription_id => true }
|
127
|
+
@client.send(:make_subscription_url, opts).should == "/user/-/apiSubscriptions/320.json"
|
128
|
+
end
|
129
|
+
|
130
|
+
it "creates the correct URL for the :sleep collection type" do
|
131
|
+
opts = { :subscription_id => "320", :type => :sleep, :use_subscription_id => true }
|
132
|
+
@client.send(:make_subscription_url, opts).should == "/user/-/sleep/apiSubscriptions/320.json"
|
133
|
+
end
|
134
|
+
|
135
|
+
it "creates the correct URL for the :body collection type" do
|
136
|
+
opts = { :subscription_id => "320", :type => :body, :use_subscription_id => true }
|
137
|
+
@client.send(:make_subscription_url, opts).should == "/user/-/body/apiSubscriptions/320.json"
|
138
|
+
end
|
139
|
+
|
140
|
+
it "creates the correct URL for the :activities collection type" do
|
141
|
+
opts = { :subscription_id => "320", :type => :activities, :use_subscription_id => true }
|
142
|
+
@client.send(:make_subscription_url, opts).should == "/user/-/activities/apiSubscriptions/320.json"
|
143
|
+
end
|
144
|
+
|
145
|
+
it "creates the correct URL for the :foods collection type" do
|
146
|
+
opts = { :subscription_id => "320", :type => :foods, :use_subscription_id => true }
|
147
|
+
@client.send(:make_subscription_url, opts).should == "/user/-/foods/apiSubscriptions/320.json"
|
148
|
+
end
|
149
|
+
|
150
|
+
it "validates the supplied subscription type" do
|
151
|
+
opts = { :subscription_id => "320" }
|
152
|
+
expect { @client.send(:make_subscription_url, opts) }.to raise_error Fitgem::InvalidArgumentError
|
153
|
+
|
154
|
+
opts[:type] = nil
|
155
|
+
expect { @client.send(:make_subscription_url, opts) }.to raise_error Fitgem::InvalidArgumentError
|
156
|
+
|
157
|
+
opts[:type] = :all
|
158
|
+
expect { @client.send(:make_subscription_url, opts) }.not_to raise_error Fitgem::InvalidArgumentError
|
159
|
+
|
160
|
+
opts[:type] = :activities
|
161
|
+
expect { @client.send(:make_subscription_url, opts) }.not_to raise_error Fitgem::InvalidArgumentError
|
162
|
+
|
163
|
+
opts[:type] = :sleep
|
164
|
+
expect { @client.send(:make_subscription_url, opts) }.not_to raise_error Fitgem::InvalidArgumentError
|
165
|
+
|
166
|
+
opts[:type] = :foods
|
167
|
+
expect { @client.send(:make_subscription_url, opts) }.not_to raise_error Fitgem::InvalidArgumentError
|
168
|
+
|
169
|
+
opts[:type] = :body
|
170
|
+
expect { @client.send(:make_subscription_url, opts) }.not_to raise_error Fitgem::InvalidArgumentError
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
data/spec/fitgem_spec.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Fitgem do
|
4
|
-
|
5
4
|
before do
|
6
|
-
@client = Fitgem::Client.new
|
5
|
+
@client = Fitgem::Client.new({
|
6
|
+
:consumer_key => '12345',
|
7
|
+
:consumer_secret => '67890'
|
8
|
+
})
|
7
9
|
end
|
8
10
|
|
9
11
|
describe "global settings" do
|
@@ -31,53 +33,4 @@ describe Fitgem do
|
|
31
33
|
@client.user_id.should == '-'
|
32
34
|
end
|
33
35
|
end
|
34
|
-
|
35
|
-
describe "data retrieval by time range" do
|
36
|
-
|
37
|
-
it 'should format the correct URI fragment based on a base date and end date' do
|
38
|
-
frag = @client.construct_date_range_fragment({:base_date => '2011-03-07', :end_date => '2011-03-14'})
|
39
|
-
frag.should == 'date/2011-03-07/2011-03-14'
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'should format the correct URI fragment based on a base date and period' do
|
43
|
-
frag = @client.construct_date_range_fragment({:base_date => '2011-03-07', :period => '7d'})
|
44
|
-
frag.should == 'date/2011-03-07/7d'
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'should raise an error unless there is a base date AND either a period or an end date' do
|
48
|
-
lambda {
|
49
|
-
@client.construct_date_range_fragment({:base_date => '2011-03-07'})
|
50
|
-
}.should raise_error(Fitgem::InvalidTimeRange)
|
51
|
-
|
52
|
-
lambda {
|
53
|
-
@client.construct_date_range_fragment({:period => '1y'})
|
54
|
-
}.should raise_error(Fitgem::InvalidTimeRange)
|
55
|
-
|
56
|
-
lambda {
|
57
|
-
@client.construct_date_range_fragment({:end_date => '2011-03-07', :period => '7d'})
|
58
|
-
}.should raise_error(Fitgem::InvalidTimeRange)
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
describe "format_date" do
|
64
|
-
it "accepts DateTime objects" do
|
65
|
-
date = DateTime.strptime('2011-03-19','%Y-%m-%d')
|
66
|
-
@client.format_date(date).should == '2011-03-19'
|
67
|
-
end
|
68
|
-
|
69
|
-
it "accepts strings in YYYY-MM-DD format" do
|
70
|
-
@client.format_date('2011-03-19').should == '2011-03-19'
|
71
|
-
end
|
72
|
-
|
73
|
-
it "accepts the string 'today' to denote the current date" do
|
74
|
-
today = Date.today.strftime("%Y-%m-%d")
|
75
|
-
@client.format_date('today').should == today
|
76
|
-
end
|
77
|
-
|
78
|
-
it "accepts the string 'yesterday' to denote the day previous to today" do
|
79
|
-
yesterday = (Date.today-1).strftime("%Y-%m-%d")
|
80
|
-
@client.format_date('yesterday').should == yesterday
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
36
|
+
end
|