fiscally 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +37 -0
- data/.rspec +3 -0
- data/.travis.yml +14 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +357 -0
- data/Rakefile +24 -0
- data/Vagrantfile +48 -0
- data/fiscally.gemspec +33 -0
- data/lib/fiscally.rb +7 -0
- data/lib/fiscally/fiscally.rb +132 -0
- data/lib/fiscally/version.rb +3 -0
- data/provision.sh +61 -0
- data/spec/fiscally_spec.rb +254 -0
- data/spec/spec_helper.rb +108 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9362c857fbcfb1bac89099d792bbf1b8973ff73d
|
4
|
+
data.tar.gz: 143390f0d2b770114241a70e25280455f8509a82
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c1cbbe76af2cd0b4171a811502b1ebd083d91ed57bf455247cc6a22833877d7029a5b269775a0eb911dc83efc0407a74ec9ad24af845abd275db192317cbd006
|
7
|
+
data.tar.gz: 1c8e61347016690a516100b3f6f1268d9e93e29383c9475b309a61fbeed781bbbb3ef426424724455b7b996916877cabe2d18873b2ba424361720bfa8ac2664b
|
data/.gitignore
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
## Virtual Machine files
|
2
|
+
/.vagrant/
|
3
|
+
|
4
|
+
*.gem
|
5
|
+
*.rbc
|
6
|
+
/.config
|
7
|
+
/coverage/
|
8
|
+
/InstalledFiles
|
9
|
+
/pkg/
|
10
|
+
/spec/reports/
|
11
|
+
/test/tmp/
|
12
|
+
/test/version_tmp/
|
13
|
+
/tmp/
|
14
|
+
|
15
|
+
## Specific to RubyMotion:
|
16
|
+
.dat*
|
17
|
+
.repl_history
|
18
|
+
build/
|
19
|
+
|
20
|
+
## Documentation cache and generated files:
|
21
|
+
/.yardoc/
|
22
|
+
/_yardoc/
|
23
|
+
/doc/
|
24
|
+
/rdoc/
|
25
|
+
|
26
|
+
## Environment normalisation:
|
27
|
+
/.bundle/
|
28
|
+
/lib/bundler/man/
|
29
|
+
|
30
|
+
# for a library or gem, you might want to ignore these files since the code is
|
31
|
+
# intended to run in multiple environments; otherwise, check them in:
|
32
|
+
Gemfile.lock
|
33
|
+
.ruby-version
|
34
|
+
.ruby-gemset
|
35
|
+
|
36
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
37
|
+
.rvmrc
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Forge Software
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,357 @@
|
|
1
|
+
# fiscally
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/fiscally.svg)](https://rubygems.org/gems/fiscally)
|
4
|
+
[![Build Status](https://travis-ci.org/forgecrafted/fiscally.svg?branch=master)](https://travis-ci.org/forgecrafted/fiscally)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/forgecrafted/fiscally/badge.svg?branch=master)](https://coveralls.io/r/forgecrafted/fiscally?branch=master)
|
6
|
+
|
7
|
+
Projects with a financial or business bent often have to deal with awareness of a fiscal year, which typically doesn't line up with a normal calendar year.
|
8
|
+
|
9
|
+
The `Fiscally` gem adds the necessary awareness to your date and time objects, covering start and end dates for fiscal years and quarterly breakdowns.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Gemfile
|
14
|
+
```
|
15
|
+
gem 'fiscally'
|
16
|
+
```
|
17
|
+
|
18
|
+
Command line
|
19
|
+
```
|
20
|
+
gem install 'fiscally'
|
21
|
+
```
|
22
|
+
|
23
|
+
### Ruby Version
|
24
|
+
|
25
|
+
Built and tested against **`2.0.0` and above**, but Probably Works™ in `1.9.3`.
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
All calculations are done **relative to the date and/or time represented by your object**. This is particularly important to remember for methods that calculate a "current" value. Our documentation examples use dates in the past to drive this point home.
|
30
|
+
|
31
|
+
Time is always set to midnight (`00:00:00`), when applicable.
|
32
|
+
|
33
|
+
Examples below use `Time`, but all methods outlined are available to `Date` and `DateTime` objects as well.
|
34
|
+
|
35
|
+
- [`#fiscal_start=`](https://github.com/forgecrafted/fiscally#fiscal_start) :boom:
|
36
|
+
- [`#fiscal_quarter`](https://github.com/forgecrafted/fiscally#fiscal_quarter)
|
37
|
+
- [`#all_quarter_months`](https://github.com/forgecrafted/fiscally#all_quarter_months)
|
38
|
+
- [`#fiscal_quarter_months`](https://github.com/forgecrafted/fiscally#fiscal_quarter_months)
|
39
|
+
- [`#first_month_of_quarter`](https://github.com/forgecrafted/fiscally#first_month_of_quarter)
|
40
|
+
- [`#first_quarter`](https://github.com/forgecrafted/fiscally#first_quarter)
|
41
|
+
- [`#second_quarter`](https://github.com/forgecrafted/fiscally#second_quarter)
|
42
|
+
- [`#third_quarter`](https://github.com/forgecrafted/fiscally#third_quarter)
|
43
|
+
- [`#fourth_quarter`](https://github.com/forgecrafted/fiscally#fourth_quarter)
|
44
|
+
- [`#beginning_of_fiscal_year`](https://github.com/forgecrafted/fiscally#beginning_of_fiscal_year)
|
45
|
+
- [`#beginning_of_fiscal_quarter`](https://github.com/forgecrafted/fiscally#beginning_of_fiscal_quarter)
|
46
|
+
- [`#end_of_fiscal_year`](https://github.com/forgecrafted/fiscally#end_of_fiscal_year)
|
47
|
+
- [`#end_of_fiscal_quarter`](https://github.com/forgecrafted/fiscally#end_of_fiscal_quarter)
|
48
|
+
- [`#quarter_starting_dates`](https://github.com/forgecrafted/fiscally#quarter_starting_dates)
|
49
|
+
- [`#quarter_ending_dates`](https://github.com/forgecrafted/fiscally#quarter_ending_dates)
|
50
|
+
|
51
|
+
### `#fiscal_start=`
|
52
|
+
###### `objekt.fiscal_start = 8`
|
53
|
+
|
54
|
+
Sets the **month** in which the fiscal calendar will start on the given `objekt`, represented as an `Integer`. The **day** is always set to the first of the month, and is immutable. Defaults to January (`fiscal_start = 1`).
|
55
|
+
|
56
|
+
This is the lynchpin of the entire system, adjusting the calendar start and end months to something other than January and December.
|
57
|
+
|
58
|
+
Calling `objekt.fiscal_start` without an assignment will return the currently set value.
|
59
|
+
|
60
|
+
This value is not frozen, and can be changed at any time.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
foo = Time.new(2014, 1)
|
64
|
+
foo.fiscal_start
|
65
|
+
# => 1
|
66
|
+
|
67
|
+
bar = Date.new(2015, 9)
|
68
|
+
bar.fiscal_start
|
69
|
+
# => 1
|
70
|
+
|
71
|
+
baz = DateTime.new(2025, 9)
|
72
|
+
baz.fiscal_start
|
73
|
+
# => 1
|
74
|
+
|
75
|
+
# this is mutable
|
76
|
+
baz.fiscal_start = 4
|
77
|
+
baz.fiscal_start
|
78
|
+
# => 4
|
79
|
+
```
|
80
|
+
|
81
|
+
### `#fiscal_quarter`
|
82
|
+
|
83
|
+
Returns an integer representing the current quarter.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
jan01 = Time.new(2015, 1)
|
87
|
+
jan01.fiscal_quarter
|
88
|
+
# => 1
|
89
|
+
|
90
|
+
jan01.fiscal_start = 4
|
91
|
+
jan01.fiscal_quarter
|
92
|
+
# => 4
|
93
|
+
|
94
|
+
jan01.fiscal_start = 10
|
95
|
+
jan01.fiscal_quarter
|
96
|
+
# => 2
|
97
|
+
```
|
98
|
+
|
99
|
+
### `#all_quarter_months`
|
100
|
+
|
101
|
+
Returns an array of arrays, the first level representing the quarters in order, followed by an array of integers representing the months within that quarter.
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
jan01 = Time.new(2015, 1)
|
105
|
+
jan01.all_quarter_months
|
106
|
+
# => [[1,2,3], [4,5,6], [7,8,9], [10,11,12]]
|
107
|
+
|
108
|
+
jan01.fiscal_start = 4
|
109
|
+
jan01.all_quarter_months
|
110
|
+
# => [[4,5,6], [7,8,9], [10,11,12], [1,2,3]]
|
111
|
+
|
112
|
+
jan01.fiscal_start = 11
|
113
|
+
jan01.all_quarter_months
|
114
|
+
# => [[11,12,1], [2,3,4], [5,6,7], [8,9,10]]
|
115
|
+
```
|
116
|
+
|
117
|
+
### `#fiscal_quarter_months`
|
118
|
+
|
119
|
+
Returns an array of integers, representing the months of the current quarter.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
jan01 = Time.new(2015, 1)
|
123
|
+
jan01.fiscal_start = 4
|
124
|
+
jan01.fiscal_quarter_months
|
125
|
+
# => [1,2,3]
|
126
|
+
|
127
|
+
jan01.fiscal_start = 2
|
128
|
+
jan01.fiscal_quarter_months
|
129
|
+
# => [11,12,1]
|
130
|
+
|
131
|
+
jan01.fiscal_start = 11
|
132
|
+
jan01.fiscal_quarter_months
|
133
|
+
# => [11,12,1]
|
134
|
+
|
135
|
+
jan01.fiscal_start = 9
|
136
|
+
jan01.fiscal_quarter_months
|
137
|
+
# => [12,1,2]
|
138
|
+
```
|
139
|
+
|
140
|
+
### `#first_month_of_quarter`
|
141
|
+
|
142
|
+
Returns an integer representing the first month of the current quarter.
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
jan01 = Time.new(2015, 1)
|
146
|
+
jan01.fiscal_start = 4
|
147
|
+
jan01.first_month_of_quarter
|
148
|
+
# => 1
|
149
|
+
|
150
|
+
jan01.fiscal_start = 2
|
151
|
+
jan01.first_month_of_quarter
|
152
|
+
# => 11
|
153
|
+
|
154
|
+
jan01.fiscal_start = 11
|
155
|
+
jan01.first_month_of_quarter
|
156
|
+
# => 11
|
157
|
+
|
158
|
+
jan01.fiscal_start = 9
|
159
|
+
jan01.first_month_of_quarter
|
160
|
+
# => 12
|
161
|
+
```
|
162
|
+
|
163
|
+
### `#first_quarter`
|
164
|
+
|
165
|
+
Returns an array of integers, representing the months of the first quarter. Aliased to `q1`.
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
jan01 = Time.new(2015, 1)
|
169
|
+
jan01.first_quarter
|
170
|
+
# => [1,2,3]
|
171
|
+
jan01.q1
|
172
|
+
# => [1,2,3]
|
173
|
+
```
|
174
|
+
|
175
|
+
### `#second_quarter`
|
176
|
+
|
177
|
+
Returns an array of integers, representing the months of the second quarter. Aliased to `q2`.
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
jan01 = Time.new(2015, 1)
|
181
|
+
jan01.second_quarter
|
182
|
+
# => [4,5,6]
|
183
|
+
jan01.q2
|
184
|
+
# => [4,5,6]
|
185
|
+
```
|
186
|
+
|
187
|
+
### `#third_quarter`
|
188
|
+
|
189
|
+
Returns an array of integers, representing the months of the third quarter. Aliased to `q3`.
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
jan01 = Time.new(2015, 1)
|
193
|
+
jan01.third_quarter
|
194
|
+
# => [7,8,9]
|
195
|
+
jan01.q3
|
196
|
+
# => [7,8,9]
|
197
|
+
```
|
198
|
+
|
199
|
+
### `#fourth_quarter`
|
200
|
+
|
201
|
+
Returns an array of integers, representing the months of the fourth quarter. Aliased to `q4`.
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
jan01 = Time.new(2015, 1)
|
205
|
+
jan01.fourth_quarter
|
206
|
+
# => [10,11,12]
|
207
|
+
jan01.q4
|
208
|
+
# => [10,11,12]
|
209
|
+
```
|
210
|
+
|
211
|
+
### `#beginning_of_fiscal_year`
|
212
|
+
|
213
|
+
Returns a new `Time`/`Date`/`DateTime` object representing the starting date of the fiscal year.
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
jan01 = Time.new(2015, 1)
|
217
|
+
jan01.fiscal_start = 3
|
218
|
+
jan01.beginning_of_fiscal_year.strftime('%F')
|
219
|
+
# => '2014-03-01'
|
220
|
+
|
221
|
+
jan01.fiscal_start = 9
|
222
|
+
jan01.beginning_of_fiscal_year.strftime('%F')
|
223
|
+
# => '2014-09-01'
|
224
|
+
|
225
|
+
jan01.fiscal_start = 12
|
226
|
+
jan01.beginning_of_fiscal_year.strftime('%F')
|
227
|
+
# => '2014-12-01'
|
228
|
+
```
|
229
|
+
|
230
|
+
The returned object will always match the original object class.
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
Time.new.beginning_of_fiscal_year.class
|
234
|
+
# => Time
|
235
|
+
Date.new.beginning_of_fiscal_year.class
|
236
|
+
# => Date
|
237
|
+
DateTime.new.beginning_of_fiscal_year.class
|
238
|
+
# => DateTime
|
239
|
+
```
|
240
|
+
|
241
|
+
### `#beginning_of_fiscal_quarter`
|
242
|
+
|
243
|
+
Returns a new `Time`/`Date`/`DateTime` object representing the starting date of the current fiscal quarter.
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
jan01 = Time.new(2015, 1)
|
247
|
+
jan01.beginning_of_fiscal_quarter.strftime('%F')
|
248
|
+
# => '2015-01-01'
|
249
|
+
|
250
|
+
jan01.fiscal_start = 4
|
251
|
+
jan01.beginning_of_fiscal_quarter.strftime('%F')
|
252
|
+
# => '2015-01-01'
|
253
|
+
|
254
|
+
jan01.fiscal_start = 2
|
255
|
+
jan01.beginning_of_fiscal_quarter.strftime('%F')
|
256
|
+
# => '2014-11-01'
|
257
|
+
|
258
|
+
jan01.fiscal_start = 11
|
259
|
+
jan01.beginning_of_fiscal_quarter.strftime('%F')
|
260
|
+
# => '2014-11-01'
|
261
|
+
|
262
|
+
jan01.fiscal_start = 9
|
263
|
+
jan01.beginning_of_fiscal_quarter.strftime('%F')
|
264
|
+
# => '2014-12-01'
|
265
|
+
```
|
266
|
+
|
267
|
+
### `#end_of_fiscal_year`
|
268
|
+
|
269
|
+
Returns a new `Time`/`Date`/`DateTime` object representing the last day of the current fiscal year.
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
jan01 = Time.new(2015, 1)
|
273
|
+
jan01.end_of_fiscal_quarter.strftime('%F')
|
274
|
+
# => '2015-03-31'
|
275
|
+
|
276
|
+
jan01.fiscal_start = 4
|
277
|
+
jan01.end_of_fiscal_quarter.strftime('%F')
|
278
|
+
# => '2015-03-31'
|
279
|
+
|
280
|
+
jan01.fiscal_start = 2
|
281
|
+
jan01.end_of_fiscal_quarter.strftime('%F')
|
282
|
+
# => '2015-01-31'
|
283
|
+
|
284
|
+
jan01.fiscal_start = 11
|
285
|
+
jan01.end_of_fiscal_quarter.strftime('%F')
|
286
|
+
# => '2015-01-31'
|
287
|
+
|
288
|
+
jan01.fiscal_start = 9
|
289
|
+
jan01.end_of_fiscal_quarter.strftime('%F')
|
290
|
+
# => '2015-02-28'
|
291
|
+
```
|
292
|
+
|
293
|
+
### `#end_of_fiscal_quarter`
|
294
|
+
|
295
|
+
Returns a new `Time`/`Date`/`DateTime` object representing the last day of the current fiscal quarter.
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
jan01 = Time.new(2015, 1)
|
299
|
+
jan01.end_of_fiscal_quarter.strftime('%F')
|
300
|
+
# => '2015-03-31'
|
301
|
+
|
302
|
+
jan01.fiscal_start = 4
|
303
|
+
jan01.end_of_fiscal_quarter.strftime('%F')
|
304
|
+
# => '2015-03-31'
|
305
|
+
|
306
|
+
jan01.fiscal_start = 2
|
307
|
+
jan01.end_of_fiscal_quarter.strftime('%F')
|
308
|
+
# => '2015-01-31'
|
309
|
+
|
310
|
+
jan01.fiscal_start = 11
|
311
|
+
jan01.end_of_fiscal_quarter.strftime('%F')
|
312
|
+
# => '2015-01-31'
|
313
|
+
|
314
|
+
jan01.fiscal_start = 9
|
315
|
+
jan01.end_of_fiscal_quarter.strftime('%F')
|
316
|
+
# => '2015-02-28'
|
317
|
+
```
|
318
|
+
|
319
|
+
### `#quarter_starting_dates`
|
320
|
+
|
321
|
+
Returns an array of `Time`/`Date`/`DateTime` objects representing the first day of each quarter for the current fiscal year.
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
jan01 = Time.new(2015, 1)
|
325
|
+
jan01.quarter_starting_dates.each { |time_obj| puts time_obj.strftime('%F') }
|
326
|
+
# => '2015-01-01'
|
327
|
+
# => '2015-04-01'
|
328
|
+
# => '2015-07-01'
|
329
|
+
# => '2015-10-01'
|
330
|
+
|
331
|
+
jan01.fiscal_start = 2
|
332
|
+
jan01.quarter_starting_dates.each { |time_obj| puts time_obj.strftime('%F') }
|
333
|
+
# => '2014-02-01'
|
334
|
+
# => '2014-05-01'
|
335
|
+
# => '2014-08-01'
|
336
|
+
# => '2014-11-01'
|
337
|
+
```
|
338
|
+
|
339
|
+
### `#quarter_ending_dates`
|
340
|
+
|
341
|
+
Returns an array of `Time`/`Date`/`DateTime` objects representing the last day of each quarter for the current fiscal year.
|
342
|
+
|
343
|
+
```ruby
|
344
|
+
jan01 = Time.new(2015, 1)
|
345
|
+
jan01.quarter_ending_dates.each { |time_obj| puts time_obj.strftime('%F') }
|
346
|
+
# => '2015-03-31'
|
347
|
+
# => '2015-06-30'
|
348
|
+
# => '2015-09-30'
|
349
|
+
# => '2015-12-31'
|
350
|
+
|
351
|
+
jan01.fiscal_start = 2
|
352
|
+
jan01.quarter_ending_dates.each { |time_obj| puts time_obj.strftime('%F') }
|
353
|
+
# => '2014-04-30'
|
354
|
+
# => '2014-07-31'
|
355
|
+
# => '2014-10-31'
|
356
|
+
# => '2015-01-31'
|
357
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
desc "Open an irb session preloaded with this library"
|
3
|
+
task :console do
|
4
|
+
sh "irb -rubygems -I lib -r fiscally.rb"
|
5
|
+
end
|
6
|
+
task :c => :console
|
7
|
+
|
8
|
+
namespace :gem do
|
9
|
+
desc "Connect to RubyGems.org account"
|
10
|
+
task :auth do
|
11
|
+
sh "curl -u forgecrafted https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Build the gem according to gemspec"
|
15
|
+
task :build do
|
16
|
+
sh "gem build fiscally.gemspec"
|
17
|
+
end
|
18
|
+
|
19
|
+
require "./lib/fiscally/version"
|
20
|
+
desc "Push the gem to RubyGems.org"
|
21
|
+
task :push do
|
22
|
+
sh "gem push fiscally-#{FinishingMoves::VERSION}.gem"
|
23
|
+
end
|
24
|
+
end
|
data/Vagrantfile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
# vi: set ft=ruby :
|
3
|
+
|
4
|
+
NAME = "fiscally"
|
5
|
+
FQDN = "#{NAME}.example.com"
|
6
|
+
|
7
|
+
Vagrant.configure("2") do |config|
|
8
|
+
# "trusty" is 14.04
|
9
|
+
config.vm.box = "trusty64"
|
10
|
+
config.vm.box_url = "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box"
|
11
|
+
|
12
|
+
# Use the normal insecure key
|
13
|
+
# https://github.com/mitchellh/vagrant/issues/2608
|
14
|
+
config.ssh.insert_key = false
|
15
|
+
|
16
|
+
config.vm.provider :virtualbox do |vb|
|
17
|
+
vb.customize ["modifyvm", :id,
|
18
|
+
# Basics.
|
19
|
+
"--name", NAME,
|
20
|
+
"--memory", 4096,
|
21
|
+
# I/O APIC must be enabled to support a multi-core guest.
|
22
|
+
"--cpus", 4,
|
23
|
+
"--ioapic", "on",
|
24
|
+
# Enable native host virtualization features (yields better performance).
|
25
|
+
"--pae", "on",
|
26
|
+
"--hwvirtex", "on",
|
27
|
+
"--largepages", "on",
|
28
|
+
"--vtxvpid", "on",
|
29
|
+
# This causes the virtual machine to proxy DNS requests through the host
|
30
|
+
# via NAT. Without this, the default resolver on the guest will introduce
|
31
|
+
# a 5 second latency on every HTTP request... which is a real downer.
|
32
|
+
"--natdnshostresolver1", "on"
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
config.vm.hostname = FQDN
|
37
|
+
config.vm.provision :shell, path: "provision.sh"
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# DONT FORGET!
|
42
|
+
#
|
43
|
+
# Force update VirtualBox Guest Additions
|
44
|
+
# Run the following command inside same directory as Vagrantfile
|
45
|
+
# Must be done once on your dev system
|
46
|
+
#
|
47
|
+
# vagrant plugin install vagrant-vbguest
|
48
|
+
#
|
data/fiscally.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require "fiscally/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
|
7
|
+
s.name = "fiscally"
|
8
|
+
s.version = Fiscally::VERSION
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["Frank Koehl"]
|
11
|
+
s.email = ["frank@forgecrafted.com", "chris@forgecrafted.com"]
|
12
|
+
s.summary = %q{Fiscal year awareness for Ruby Date and Time classes.}
|
13
|
+
s.description = <<-EOF
|
14
|
+
Projects with a financial or business bent often have to deal with awareness of a fiscal year, which typically doesn't line up with a normal calendar year.
|
15
|
+
|
16
|
+
The `Fiscally` gem adds the necessary awareness to your date and time objects, covering start and end dates for fiscal years and quarterly breakdowns.
|
17
|
+
EOF
|
18
|
+
s.homepage = "https://github.com/forgecrafted/fiscally"
|
19
|
+
s.license = "MIT"
|
20
|
+
|
21
|
+
s.files = `git ls-files -z`.split("\x0")
|
22
|
+
s.test_files = Dir["spec/**/*"]
|
23
|
+
|
24
|
+
s.add_development_dependency 'rb-readline', '>= 0'
|
25
|
+
s.add_development_dependency 'rspec', '~> 3.1.0'
|
26
|
+
s.add_development_dependency 'priscilla', '>= 0'
|
27
|
+
s.add_development_dependency 'pry-byebug', '>= 0'
|
28
|
+
s.add_development_dependency 'fuubar', '>= 0'
|
29
|
+
s.add_development_dependency 'coveralls', '>= 0.8.0'
|
30
|
+
|
31
|
+
s.required_ruby_version = '>= 2.0.0'
|
32
|
+
|
33
|
+
end
|
data/lib/fiscally.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module FiscallyGemMethods
|
4
|
+
|
5
|
+
def fiscal_start
|
6
|
+
@fiscal_start = 1 if @fiscal_start.nil?
|
7
|
+
@fiscal_start
|
8
|
+
end
|
9
|
+
|
10
|
+
def fiscal_start=(month)
|
11
|
+
@fiscal_start = Integer(month)
|
12
|
+
end
|
13
|
+
|
14
|
+
def fiscal_quarter
|
15
|
+
((( self.month + ( 12 - fiscal_start ) ) / 3 ) % 4 ) + 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def all_quarter_months
|
19
|
+
quarters = []
|
20
|
+
this_quarter = []
|
21
|
+
this_month = fiscal_start
|
22
|
+
(1..12).each do |n|
|
23
|
+
this_quarter << this_month
|
24
|
+
if n % 3 == 0
|
25
|
+
quarters << this_quarter
|
26
|
+
this_quarter = []
|
27
|
+
end
|
28
|
+
this_month = this_month < 12 ? this_month + 1 : 1
|
29
|
+
end
|
30
|
+
quarters
|
31
|
+
end
|
32
|
+
alias_method :fiscal_calendar, :all_quarter_months
|
33
|
+
|
34
|
+
def fiscal_quarter_months
|
35
|
+
all_quarter_months[fiscal_quarter - 1]
|
36
|
+
end
|
37
|
+
|
38
|
+
def first_month_of_quarter
|
39
|
+
fiscal_quarter_months[0]
|
40
|
+
end
|
41
|
+
|
42
|
+
def first_quarter
|
43
|
+
all_quarter_months[0]
|
44
|
+
end
|
45
|
+
alias_method :q1, :first_quarter
|
46
|
+
|
47
|
+
def second_quarter
|
48
|
+
all_quarter_months[1]
|
49
|
+
end
|
50
|
+
alias_method :q2, :second_quarter
|
51
|
+
|
52
|
+
def third_quarter
|
53
|
+
all_quarter_months[2]
|
54
|
+
end
|
55
|
+
alias_method :q3, :third_quarter
|
56
|
+
|
57
|
+
def fourth_quarter
|
58
|
+
all_quarter_months[3]
|
59
|
+
end
|
60
|
+
alias_method :q4, :fourth_quarter
|
61
|
+
|
62
|
+
def beginning_of_fiscal_year
|
63
|
+
self.class.new _last_fiscal_year_for_month(q1[0]), q1[0]
|
64
|
+
end
|
65
|
+
|
66
|
+
def beginning_of_fiscal_quarter
|
67
|
+
self.class.new _last_fiscal_year_for_month(first_month_of_quarter), first_month_of_quarter
|
68
|
+
end
|
69
|
+
|
70
|
+
def end_of_fiscal_year
|
71
|
+
if self.class.name == 'Time'
|
72
|
+
d = beginning_of_fiscal_year + (60*60*24*385)
|
73
|
+
self.class.new(d.year, d.month) - (60*60*24)
|
74
|
+
else
|
75
|
+
d = beginning_of_fiscal_year >> 12
|
76
|
+
self.class.new(d.year, d.month) - 1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def end_of_fiscal_quarter
|
81
|
+
if self.class.name == 'Time'
|
82
|
+
d = beginning_of_fiscal_quarter + (60*60*24*100)
|
83
|
+
self.class.new(d.year, d.month) - (60*60*24)
|
84
|
+
else
|
85
|
+
d = beginning_of_fiscal_quarter >> 3
|
86
|
+
self.class.new(d.year, d.month) - 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def quarter_starting_dates
|
91
|
+
ret = []
|
92
|
+
all_quarter_months.map { |months| months[0] }.each do |m|
|
93
|
+
y = m >= fiscal_start ? starting_year : starting_year + 1
|
94
|
+
ret << self.class.new(y, m)
|
95
|
+
end
|
96
|
+
ret
|
97
|
+
end
|
98
|
+
|
99
|
+
def quarter_ending_dates
|
100
|
+
ret = []
|
101
|
+
all_quarter_months.map { |months| months[2] }.each do |m|
|
102
|
+
y = m >= fiscal_start ? starting_year : starting_year + 1
|
103
|
+
if self.class.name == 'Time'
|
104
|
+
d = self.class.new(y, m) + (60*60*24*42)
|
105
|
+
ret << self.class.new(d.year, d.month) - (60*60*24)
|
106
|
+
else
|
107
|
+
d = self.class.new(y, m) >> 1
|
108
|
+
ret << self.class.new(d.year, d.month) - 1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
ret
|
112
|
+
end
|
113
|
+
|
114
|
+
protected
|
115
|
+
|
116
|
+
def starting_year
|
117
|
+
fiscal_start > 1 ? self.year - 1 : self.year
|
118
|
+
end
|
119
|
+
|
120
|
+
def _last_fiscal_year_for_month(month)
|
121
|
+
if month == fiscal_start && month == self.month
|
122
|
+
self.year
|
123
|
+
else
|
124
|
+
month >= fiscal_start ? self.year - 1 : self.year
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
Time.send :include, FiscallyGemMethods
|
131
|
+
Date.send :include, FiscallyGemMethods
|
132
|
+
DateTime.send :include, FiscallyGemMethods
|
data/provision.sh
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Shell user settings.
|
4
|
+
USER_NAME=vagrant
|
5
|
+
USER_HOME=/home/$USER_NAME
|
6
|
+
DEFAULT_RUBY='2.2.2'
|
7
|
+
|
8
|
+
###############################################################################
|
9
|
+
# Functions
|
10
|
+
###############################################################################
|
11
|
+
# Most of the time we can get by with this DRY wrapper for sudo commands.
|
12
|
+
as_user() {
|
13
|
+
echo "$USER_NAME:~$ > ${*}"
|
14
|
+
su -l $USER_NAME -c "$*"
|
15
|
+
}
|
16
|
+
|
17
|
+
###############################################################################
|
18
|
+
# Base System
|
19
|
+
###############################################################################
|
20
|
+
apt-get -y update
|
21
|
+
apt-get -yfV dist-upgrade
|
22
|
+
|
23
|
+
###############################################################################
|
24
|
+
# rbenv & ruby-build, and Rubies
|
25
|
+
# From https://github.com/sstephenson/rbenv
|
26
|
+
# and https://github.com/sstephenson/ruby-build
|
27
|
+
###############################################################################
|
28
|
+
|
29
|
+
# Install dependencies.
|
30
|
+
apt-get install -yfV \
|
31
|
+
build-essential \
|
32
|
+
curl \
|
33
|
+
git-core \
|
34
|
+
libcurl4-openssl-dev \
|
35
|
+
libreadline-dev \
|
36
|
+
libsqlite3-dev \
|
37
|
+
libssl-dev \
|
38
|
+
libxml2-dev \
|
39
|
+
libxslt1-dev \
|
40
|
+
libyaml-dev \
|
41
|
+
python-software-properties \
|
42
|
+
sqlite3 \
|
43
|
+
zlib1g-dev \
|
44
|
+
|
45
|
+
# Install rbenv and ruby-build.
|
46
|
+
as_user "git clone https://github.com/sstephenson/rbenv.git $USER_HOME/.rbenv"
|
47
|
+
as_user "git clone https://github.com/sstephenson/ruby-build.git $USER_HOME/.rbenv/plugins/ruby-build"
|
48
|
+
|
49
|
+
# Setup bash to use rbenv for $USER_NAME.
|
50
|
+
truncate -s 0 $USER_HOME/.bashrc
|
51
|
+
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> $USER_HOME/.bashrc
|
52
|
+
echo 'eval "$(rbenv init -)"' >> $USER_HOME/.bashrc
|
53
|
+
echo 'cd /vagrant' >> $USER_HOME/.bashrc
|
54
|
+
|
55
|
+
echo 'gem: --no-document' > $USER_HOME/.gemrc
|
56
|
+
|
57
|
+
# Install the requested version of Ruby, with Bundler.
|
58
|
+
as_user "rbenv install -s $DEFAULT_RUBY"
|
59
|
+
as_user "rbenv global $DEFAULT_RUBY"
|
60
|
+
as_user "RBENV_VERSION=$DEFAULT_RUBY gem install bundler"
|
61
|
+
as_user "cd /vagrant && bundle"
|
@@ -0,0 +1,254 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Fiscally' do
|
4
|
+
|
5
|
+
let(:default_quarters) { [[1,2,3], [4,5,6], [7,8,9], [10,11,12]] }
|
6
|
+
|
7
|
+
before(:example) do
|
8
|
+
@jan01 = Time.new(2015, 1)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "is included into Time, Date, and DateTime" do
|
12
|
+
expect(Time.method_defined? :fiscal_start).to be true
|
13
|
+
expect(Time.method_defined? :fiscal_quarter).to be true
|
14
|
+
expect(Time.method_defined? :all_quarter_months).to be true
|
15
|
+
expect(Time.method_defined? :fiscal_quarter_months).to be true
|
16
|
+
|
17
|
+
expect(Date.method_defined? :fiscal_start).to be true
|
18
|
+
expect(Date.method_defined? :fiscal_quarter).to be true
|
19
|
+
expect(Date.method_defined? :all_quarter_months).to be true
|
20
|
+
expect(Date.method_defined? :fiscal_quarter_months).to be true
|
21
|
+
|
22
|
+
expect(DateTime.method_defined? :fiscal_start).to be true
|
23
|
+
expect(DateTime.method_defined? :fiscal_quarter).to be true
|
24
|
+
expect(DateTime.method_defined? :all_quarter_months).to be true
|
25
|
+
expect(DateTime.method_defined? :fiscal_quarter_months).to be true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "#fiscal_quarter" do
|
29
|
+
q = 1
|
30
|
+
default_quarters.each do |months|
|
31
|
+
months.each { |m| expect(Time.new(2015, m).fiscal_quarter).to eq q }
|
32
|
+
q += 1
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adjusted fiscal calendar
|
36
|
+
@jan01.fiscal_start = 4
|
37
|
+
expect(@jan01.fiscal_quarter).to eq 4
|
38
|
+
@jan01.fiscal_start = 10
|
39
|
+
expect(@jan01.fiscal_quarter).to eq 2
|
40
|
+
|
41
|
+
# Make sure other date classes work
|
42
|
+
expect(Date.new(2015, 9).fiscal_quarter).to eq 3
|
43
|
+
expect(DateTime.new(2015, 9).fiscal_quarter).to eq 3
|
44
|
+
end
|
45
|
+
|
46
|
+
it "#all_quarter_months" do
|
47
|
+
expect(@jan01.all_quarter_months).to eq default_quarters
|
48
|
+
@jan01.fiscal_start = 4
|
49
|
+
expect(@jan01.all_quarter_months).to eq [[4,5,6], [7,8,9], [10,11,12], [1,2,3]]
|
50
|
+
@jan01.fiscal_start = 11
|
51
|
+
expect(@jan01.all_quarter_months).to eq [[11,12,1], [2,3,4], [5,6,7], [8,9,10]]
|
52
|
+
|
53
|
+
expect(Date.new(2015, 9).all_quarter_months).to eq default_quarters
|
54
|
+
expect(DateTime.new(2015, 9).all_quarter_months).to eq default_quarters
|
55
|
+
# alias check
|
56
|
+
expect(DateTime.new(2015, 9).fiscal_calendar).to eq default_quarters
|
57
|
+
end
|
58
|
+
|
59
|
+
it "#quarter shortcuts" do
|
60
|
+
expect(@jan01.first_quarter).to eq default_quarters[0]
|
61
|
+
expect(@jan01.q1).to eq default_quarters[0]
|
62
|
+
expect(@jan01.second_quarter).to eq default_quarters[1]
|
63
|
+
expect(@jan01.q2).to eq default_quarters[1]
|
64
|
+
expect(@jan01.third_quarter).to eq default_quarters[2]
|
65
|
+
expect(@jan01.q3).to eq default_quarters[2]
|
66
|
+
expect(@jan01.fourth_quarter).to eq default_quarters[3]
|
67
|
+
expect(@jan01.q4).to eq default_quarters[3]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "#fiscal_quarter_months" do
|
71
|
+
expect(Time.new(2015, 1).fiscal_quarter_months).to eq [1,2,3]
|
72
|
+
expect(Date.new(2015, 2).fiscal_quarter_months).to eq [1,2,3]
|
73
|
+
expect(DateTime.new(2015, 5).fiscal_quarter_months).to eq [4,5,6]
|
74
|
+
|
75
|
+
@jan01.fiscal_start = 4
|
76
|
+
expect(@jan01.fiscal_quarter_months).to eq [1,2,3]
|
77
|
+
@jan01.fiscal_start = 2
|
78
|
+
expect(@jan01.fiscal_quarter_months).to eq [11,12,1]
|
79
|
+
@jan01.fiscal_start = 11
|
80
|
+
expect(@jan01.fiscal_quarter_months).to eq [11,12,1]
|
81
|
+
@jan01.fiscal_start = 9
|
82
|
+
expect(@jan01.fiscal_quarter_months).to eq [12,1,2]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "#first_month_of_quarter" do
|
86
|
+
expect(Time.new(2015, 1).first_month_of_quarter).to eq 1
|
87
|
+
expect(Date.new(2015, 8).first_month_of_quarter).to eq 7
|
88
|
+
expect(DateTime.new(2015, 5).first_month_of_quarter).to eq 4
|
89
|
+
|
90
|
+
@jan01.fiscal_start = 4
|
91
|
+
expect(@jan01.first_month_of_quarter).to eq 1
|
92
|
+
@jan01.fiscal_start = 2
|
93
|
+
expect(@jan01.first_month_of_quarter).to eq 11
|
94
|
+
@jan01.fiscal_start = 11
|
95
|
+
expect(@jan01.first_month_of_quarter).to eq 11
|
96
|
+
@jan01.fiscal_start = 9
|
97
|
+
expect(@jan01.first_month_of_quarter).to eq 12
|
98
|
+
end
|
99
|
+
|
100
|
+
it "#beginning_of_fiscal_quarter" do
|
101
|
+
expect(Time.new.beginning_of_fiscal_quarter).to be_a Time
|
102
|
+
expect(Date.new.beginning_of_fiscal_quarter).to be_a Date
|
103
|
+
expect(DateTime.new.beginning_of_fiscal_quarter).to be_a DateTime
|
104
|
+
|
105
|
+
expect(@jan01.beginning_of_fiscal_quarter.strftime('%F')).to eq '2015-01-01'
|
106
|
+
@jan01.fiscal_start = 4
|
107
|
+
expect(@jan01.beginning_of_fiscal_quarter.strftime('%F')).to eq '2015-01-01'
|
108
|
+
@jan01.fiscal_start = 2
|
109
|
+
expect(@jan01.beginning_of_fiscal_quarter.strftime('%F')).to eq '2014-11-01'
|
110
|
+
@jan01.fiscal_start = 11
|
111
|
+
expect(@jan01.beginning_of_fiscal_quarter.strftime('%F')).to eq '2014-11-01'
|
112
|
+
@jan01.fiscal_start = 9
|
113
|
+
expect(@jan01.beginning_of_fiscal_quarter.strftime('%F')).to eq '2014-12-01'
|
114
|
+
end
|
115
|
+
|
116
|
+
it "#end_of_fiscal_quarter" do
|
117
|
+
expect(Time.new.end_of_fiscal_quarter).to be_a Time
|
118
|
+
expect(Date.new.end_of_fiscal_quarter).to be_a Date
|
119
|
+
expect(DateTime.new.end_of_fiscal_quarter).to be_a DateTime
|
120
|
+
|
121
|
+
expect(@jan01.end_of_fiscal_quarter.strftime('%F')).to eq '2015-03-31'
|
122
|
+
@jan01.fiscal_start = 4
|
123
|
+
expect(@jan01.end_of_fiscal_quarter.strftime('%F')).to eq '2015-03-31'
|
124
|
+
@jan01.fiscal_start = 2
|
125
|
+
expect(@jan01.end_of_fiscal_quarter.strftime('%F')).to eq '2015-01-31'
|
126
|
+
@jan01.fiscal_start = 11
|
127
|
+
expect(@jan01.end_of_fiscal_quarter.strftime('%F')).to eq '2015-01-31'
|
128
|
+
@jan01.fiscal_start = 9
|
129
|
+
expect(@jan01.end_of_fiscal_quarter.strftime('%F')).to eq '2015-02-28'
|
130
|
+
|
131
|
+
# Separate logic for Date and DateTime means separate tests
|
132
|
+
@jan01_date = Date.new(2015, 1)
|
133
|
+
expect(@jan01_date.end_of_fiscal_quarter.strftime('%F')).to eq '2015-03-31'
|
134
|
+
@jan01_date.fiscal_start = 4
|
135
|
+
expect(@jan01_date.end_of_fiscal_quarter.strftime('%F')).to eq '2015-03-31'
|
136
|
+
@jan01_date.fiscal_start = 2
|
137
|
+
expect(@jan01_date.end_of_fiscal_quarter.strftime('%F')).to eq '2015-01-31'
|
138
|
+
@jan01_date.fiscal_start = 11
|
139
|
+
expect(@jan01_date.end_of_fiscal_quarter.strftime('%F')).to eq '2015-01-31'
|
140
|
+
@jan01_date.fiscal_start = 9
|
141
|
+
expect(@jan01_date.end_of_fiscal_quarter.strftime('%F')).to eq '2015-02-28'
|
142
|
+
|
143
|
+
@jan01_datetime = DateTime.new(2015, 1)
|
144
|
+
expect(@jan01_datetime.end_of_fiscal_quarter.strftime('%F')).to eq '2015-03-31'
|
145
|
+
@jan01_datetime.fiscal_start = 4
|
146
|
+
expect(@jan01_datetime.end_of_fiscal_quarter.strftime('%F')).to eq '2015-03-31'
|
147
|
+
@jan01_datetime.fiscal_start = 2
|
148
|
+
expect(@jan01_datetime.end_of_fiscal_quarter.strftime('%F')).to eq '2015-01-31'
|
149
|
+
@jan01_datetime.fiscal_start = 11
|
150
|
+
expect(@jan01_datetime.end_of_fiscal_quarter.strftime('%F')).to eq '2015-01-31'
|
151
|
+
@jan01_datetime.fiscal_start = 9
|
152
|
+
expect(@jan01_datetime.end_of_fiscal_quarter.strftime('%F')).to eq '2015-02-28'
|
153
|
+
end
|
154
|
+
|
155
|
+
it "#beginning_of_fiscal_year" do
|
156
|
+
expect(Time.new.beginning_of_fiscal_year).to be_a Time
|
157
|
+
expect(Date.new.beginning_of_fiscal_year).to be_a Date
|
158
|
+
expect(DateTime.new.beginning_of_fiscal_year).to be_a DateTime
|
159
|
+
|
160
|
+
@jan01.fiscal_start = 3
|
161
|
+
expect(@jan01.beginning_of_fiscal_year.strftime('%F')).to eq '2014-03-01'
|
162
|
+
@jan01.fiscal_start = 9
|
163
|
+
expect(@jan01.beginning_of_fiscal_year.strftime('%F')).to eq '2014-09-01'
|
164
|
+
@jan01.fiscal_start = 12
|
165
|
+
expect(@jan01.beginning_of_fiscal_year.strftime('%F')).to eq '2014-12-01'
|
166
|
+
end
|
167
|
+
|
168
|
+
it "#end_of_fiscal_year" do
|
169
|
+
expect(Time.new.end_of_fiscal_year).to be_a Time
|
170
|
+
expect(Date.new.end_of_fiscal_year).to be_a Date
|
171
|
+
expect(DateTime.new.end_of_fiscal_year).to be_a DateTime
|
172
|
+
|
173
|
+
@jan01.fiscal_start = 3
|
174
|
+
expect(@jan01.end_of_fiscal_year.strftime('%F')).to eq '2015-02-28'
|
175
|
+
@jan01.fiscal_start = 9
|
176
|
+
expect(@jan01.end_of_fiscal_year.strftime('%F')).to eq '2015-08-31'
|
177
|
+
@jan01.fiscal_start = 12
|
178
|
+
expect(@jan01.end_of_fiscal_year.strftime('%F')).to eq '2015-11-30'
|
179
|
+
|
180
|
+
# Separate logic for Date and DateTime means separate tests
|
181
|
+
@jan01_date = Date.new(2015, 1)
|
182
|
+
@jan01_date.fiscal_start = 3
|
183
|
+
expect(@jan01_date.end_of_fiscal_year.strftime('%F')).to eq '2015-02-28'
|
184
|
+
@jan01_date.fiscal_start = 9
|
185
|
+
expect(@jan01_date.end_of_fiscal_year.strftime('%F')).to eq '2015-08-31'
|
186
|
+
@jan01_date.fiscal_start = 12
|
187
|
+
expect(@jan01_date.end_of_fiscal_year.strftime('%F')).to eq '2015-11-30'
|
188
|
+
|
189
|
+
@jan01_datetime = DateTime.new(2015, 1)
|
190
|
+
@jan01_datetime.fiscal_start = 3
|
191
|
+
expect(@jan01_datetime.end_of_fiscal_year.strftime('%F')).to eq '2015-02-28'
|
192
|
+
@jan01_datetime.fiscal_start = 9
|
193
|
+
expect(@jan01_datetime.end_of_fiscal_year.strftime('%F')).to eq '2015-08-31'
|
194
|
+
@jan01_datetime.fiscal_start = 12
|
195
|
+
expect(@jan01_datetime.end_of_fiscal_year.strftime('%F')).to eq '2015-11-30'
|
196
|
+
end
|
197
|
+
|
198
|
+
it "#quarter_starting_dates" do
|
199
|
+
expect(@jan01.quarter_starting_dates).to be_an Array
|
200
|
+
expect(@jan01.quarter_starting_dates.length).to eq 4
|
201
|
+
|
202
|
+
expect(@jan01.quarter_starting_dates[0].strftime('%F')).to eq '2015-01-01'
|
203
|
+
expect(@jan01.quarter_starting_dates[1].strftime('%F')).to eq '2015-04-01'
|
204
|
+
expect(@jan01.quarter_starting_dates[2].strftime('%F')).to eq '2015-07-01'
|
205
|
+
expect(@jan01.quarter_starting_dates[3].strftime('%F')).to eq '2015-10-01'
|
206
|
+
|
207
|
+
@jan01.fiscal_start = 2
|
208
|
+
expect(@jan01.quarter_starting_dates[0].strftime('%F')).to eq '2014-02-01'
|
209
|
+
expect(@jan01.quarter_starting_dates[1].strftime('%F')).to eq '2014-05-01'
|
210
|
+
expect(@jan01.quarter_starting_dates[2].strftime('%F')).to eq '2014-08-01'
|
211
|
+
expect(@jan01.quarter_starting_dates[3].strftime('%F')).to eq '2014-11-01'
|
212
|
+
|
213
|
+
@jan01.fiscal_start = 9
|
214
|
+
expect(@jan01.quarter_starting_dates[0].strftime('%F')).to eq '2014-09-01'
|
215
|
+
expect(@jan01.quarter_starting_dates[1].strftime('%F')).to eq '2014-12-01'
|
216
|
+
expect(@jan01.quarter_starting_dates[2].strftime('%F')).to eq '2015-03-01'
|
217
|
+
expect(@jan01.quarter_starting_dates[3].strftime('%F')).to eq '2015-06-01'
|
218
|
+
|
219
|
+
@jan01_date = Date.new(2015, 1)
|
220
|
+
expect(@jan01_date.quarter_starting_dates[0].strftime('%F')).to eq '2015-01-01'
|
221
|
+
expect(@jan01_date.quarter_starting_dates[1].strftime('%F')).to eq '2015-04-01'
|
222
|
+
expect(@jan01_date.quarter_starting_dates[2].strftime('%F')).to eq '2015-07-01'
|
223
|
+
expect(@jan01_date.quarter_starting_dates[3].strftime('%F')).to eq '2015-10-01'
|
224
|
+
end
|
225
|
+
|
226
|
+
it "#quarter_ending_dates" do
|
227
|
+
expect(@jan01.quarter_ending_dates).to be_an Array
|
228
|
+
expect(@jan01.quarter_ending_dates.length).to eq 4
|
229
|
+
|
230
|
+
expect(@jan01.quarter_ending_dates[0].strftime('%F')).to eq '2015-03-31'
|
231
|
+
expect(@jan01.quarter_ending_dates[1].strftime('%F')).to eq '2015-06-30'
|
232
|
+
expect(@jan01.quarter_ending_dates[2].strftime('%F')).to eq '2015-09-30'
|
233
|
+
expect(@jan01.quarter_ending_dates[3].strftime('%F')).to eq '2015-12-31'
|
234
|
+
|
235
|
+
@jan01.fiscal_start = 2
|
236
|
+
expect(@jan01.quarter_ending_dates[0].strftime('%F')).to eq '2014-04-30'
|
237
|
+
expect(@jan01.quarter_ending_dates[1].strftime('%F')).to eq '2014-07-31'
|
238
|
+
expect(@jan01.quarter_ending_dates[2].strftime('%F')).to eq '2014-10-31'
|
239
|
+
expect(@jan01.quarter_ending_dates[3].strftime('%F')).to eq '2015-01-31'
|
240
|
+
|
241
|
+
@jan01.fiscal_start = 9
|
242
|
+
expect(@jan01.quarter_ending_dates[0].strftime('%F')).to eq '2014-11-30'
|
243
|
+
expect(@jan01.quarter_ending_dates[1].strftime('%F')).to eq '2015-02-28'
|
244
|
+
expect(@jan01.quarter_ending_dates[2].strftime('%F')).to eq '2015-05-31'
|
245
|
+
expect(@jan01.quarter_ending_dates[3].strftime('%F')).to eq '2015-08-31'
|
246
|
+
|
247
|
+
@jan01_date = Date.new(2015, 1)
|
248
|
+
expect(@jan01_date.quarter_ending_dates[0].strftime('%F')).to eq '2015-03-31'
|
249
|
+
expect(@jan01_date.quarter_ending_dates[1].strftime('%F')).to eq '2015-06-30'
|
250
|
+
expect(@jan01_date.quarter_ending_dates[2].strftime('%F')).to eq '2015-09-30'
|
251
|
+
expect(@jan01_date.quarter_ending_dates[3].strftime('%F')).to eq '2015-12-31'
|
252
|
+
end
|
253
|
+
|
254
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
Bundler.setup
|
5
|
+
|
6
|
+
require 'priscilla'
|
7
|
+
require 'pry'
|
8
|
+
require 'byebug'
|
9
|
+
|
10
|
+
require 'coveralls'
|
11
|
+
Coveralls.wear!
|
12
|
+
|
13
|
+
require 'fiscally'
|
14
|
+
|
15
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
16
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
17
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
18
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
19
|
+
#
|
20
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
21
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
22
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
23
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
24
|
+
# a separate helper file that requires the additional dependencies and performs
|
25
|
+
# the additional setup, and require it from the spec files that actually need it.
|
26
|
+
#
|
27
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
28
|
+
# users commonly want.
|
29
|
+
#
|
30
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
31
|
+
RSpec.configure do |config|
|
32
|
+
# rspec-expectations config goes here. You can use an alternate
|
33
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
34
|
+
# assertions if you prefer.
|
35
|
+
config.expect_with :rspec do |expectations|
|
36
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
37
|
+
# and `failure_message` of custom matchers include text for helper methods
|
38
|
+
# defined using `chain`, e.g.:
|
39
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
40
|
+
# # => "be bigger than 2 and smaller than 4"
|
41
|
+
# ...rather than:
|
42
|
+
# # => "be bigger than 2"
|
43
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
44
|
+
end
|
45
|
+
|
46
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
47
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
48
|
+
config.mock_with :rspec do |mocks|
|
49
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
50
|
+
# a real object. This is generally recommended, and will default to
|
51
|
+
# `true` in RSpec 4.
|
52
|
+
mocks.verify_partial_doubles = true
|
53
|
+
end
|
54
|
+
|
55
|
+
config.filter_run :focus
|
56
|
+
config.run_all_when_everything_filtered = true
|
57
|
+
|
58
|
+
config.order = :random
|
59
|
+
|
60
|
+
# The settings below are suggested to provide a good initial experience
|
61
|
+
# with RSpec, but feel free to customize to your heart's content.
|
62
|
+
=begin
|
63
|
+
# These two settings work together to allow you to limit a spec run
|
64
|
+
# to individual examples or groups you care about by tagging them with
|
65
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
66
|
+
# get run.
|
67
|
+
config.filter_run :focus
|
68
|
+
config.run_all_when_everything_filtered = true
|
69
|
+
|
70
|
+
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
71
|
+
# For more details, see:
|
72
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
73
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
74
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
75
|
+
config.disable_monkey_patching!
|
76
|
+
|
77
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
78
|
+
# be too noisy due to issues in dependencies.
|
79
|
+
config.warnings = true
|
80
|
+
|
81
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
82
|
+
# file, and it's useful to allow more verbose output when running an
|
83
|
+
# individual spec file.
|
84
|
+
if config.files_to_run.one?
|
85
|
+
# Use the documentation formatter for detailed output,
|
86
|
+
# unless a formatter has already been configured
|
87
|
+
# (e.g. via a command-line flag).
|
88
|
+
config.default_formatter = 'doc'
|
89
|
+
end
|
90
|
+
|
91
|
+
# Print the 10 slowest examples and example groups at the
|
92
|
+
# end of the spec run, to help surface which specs are running
|
93
|
+
# particularly slow.
|
94
|
+
config.profile_examples = 10
|
95
|
+
|
96
|
+
# Run specs in random order to surface order dependencies. If you find an
|
97
|
+
# order dependency and want to debug it, you can fix the order by providing
|
98
|
+
# the seed, which is printed after each run.
|
99
|
+
# --seed 1234
|
100
|
+
config.order = :random
|
101
|
+
|
102
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
103
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
104
|
+
# test failures related to randomization by passing the same `--seed` value
|
105
|
+
# as the one that triggered the failure.
|
106
|
+
Kernel.srand config.seed
|
107
|
+
=end
|
108
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fiscally
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Frank Koehl
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-09-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rb-readline
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.1.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.1.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: priscilla
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry-byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: fuubar
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.8.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.8.0
|
97
|
+
description: |2
|
98
|
+
Projects with a financial or business bent often have to deal with awareness of a fiscal year, which typically doesn't line up with a normal calendar year.
|
99
|
+
|
100
|
+
The `Fiscally` gem adds the necessary awareness to your date and time objects, covering start and end dates for fiscal years and quarterly breakdowns.
|
101
|
+
email:
|
102
|
+
- frank@forgecrafted.com
|
103
|
+
- chris@forgecrafted.com
|
104
|
+
executables: []
|
105
|
+
extensions: []
|
106
|
+
extra_rdoc_files: []
|
107
|
+
files:
|
108
|
+
- ".gitignore"
|
109
|
+
- ".rspec"
|
110
|
+
- ".travis.yml"
|
111
|
+
- Gemfile
|
112
|
+
- LICENSE
|
113
|
+
- README.md
|
114
|
+
- Rakefile
|
115
|
+
- Vagrantfile
|
116
|
+
- fiscally.gemspec
|
117
|
+
- lib/fiscally.rb
|
118
|
+
- lib/fiscally/fiscally.rb
|
119
|
+
- lib/fiscally/version.rb
|
120
|
+
- provision.sh
|
121
|
+
- spec/fiscally_spec.rb
|
122
|
+
- spec/spec_helper.rb
|
123
|
+
homepage: https://github.com/forgecrafted/fiscally
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
metadata: {}
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: 2.0.0
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
requirements: []
|
142
|
+
rubyforge_project:
|
143
|
+
rubygems_version: 2.4.5
|
144
|
+
signing_key:
|
145
|
+
specification_version: 4
|
146
|
+
summary: Fiscal year awareness for Ruby Date and Time classes.
|
147
|
+
test_files:
|
148
|
+
- spec/fiscally_spec.rb
|
149
|
+
- spec/spec_helper.rb
|