in_business 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Tim Rogers
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # in_business
2
+
3
+ Working with business opening times is pretty hard, or at least, a lot of effort
4
+ to write for each application.
5
+
6
+ We have awesome gems like
7
+ [business_time](https://github.com/bokmann/business_time) which help, but there's
8
+ a gap in the market for something that knows your hours, and can quickly tell
9
+ you whether a particular time is open or closed concretely. As fun as crazy
10
+ time calculations are!
11
+
12
+ I've been doing quite a lot of work recently building [Twilio](http://www.twilio.com)
13
+ apps such as my startup employer [GoCardless](https://gocardless.com)'s
14
+ [Nodephone](https://gocardless.com/blog/data-driven-support/) where you need
15
+ to work out if an incoming call is during office hours or not.
16
+
17
+ This gem allows you to do that kind of thing with simple `.open?` and `.closed?`
18
+ methods, and even supports holidays!
19
+
20
+ *in_business* depends on Rails's [ActiveSupport](https://github.com/rails/rails/tree/master/activesupport)
21
+ and is awesome combined with the [holidays](https://github.com/alexdunae/holidays)
22
+ gem.
23
+
24
+ ## Usage
25
+
26
+ ```ruby
27
+ InBusiness.open? DateTime.now # => nil (since we've not set any hours yet!)
28
+
29
+ # We want to be open 9am til 6pm on a Monday
30
+ InBusiness.hours.monday = "09:00".."18:00"
31
+ InBusiness.open? DateTime.parse('10am Monday') # => true
32
+ InBusiness.closed? DateTime.parse('9pm Monday') # => true
33
+
34
+ # In our imaginary land, 8th July 2013 is a Monday but a public holiday, so let's add it...
35
+ InBusiness.holidays << Date.parse('8th July 2013')
36
+ InBusiness.hours.monday # => "09:00".."18:00"
37
+ InBusiness.open? DateTime.parse("8th July 2013 12:00") # => false
38
+ InBusiness.is_holiday? DateTime.parse("8th July 2013 12:00") # => true
39
+ ```
40
+
41
+ ## Installation
42
+
43
+ Add this line to your application's Gemfile:
44
+
45
+ `gem 'in_business'`
46
+
47
+ And then execute:
48
+
49
+ `$ bundle`
50
+
51
+ Or install it yourself as:
52
+
53
+ `$ gem install in_business`
54
+
55
+ ### Using with Rails
56
+
57
+ Make sure that the gem is in your Gemfile and that you've `bundle install` -ed,
58
+ then create an initializer, for example `config/initializers/in_business.rb`.
59
+
60
+ In there, you'll want to set your daily hours and any holidays:
61
+
62
+ ```ruby
63
+ InBusiness.hours = {
64
+ monday: "09:00".."18:00",
65
+ tuesday: "10:00".."19:00",
66
+ # ...
67
+ saturday: "09:00".."12:00"
68
+ }
69
+
70
+ InBusiness.holidays << Date.parse("25th December 2013")
71
+ ```
72
+
73
+ ### Using with the [holidays](https://github.com/alexdunae/holidays) gem
74
+
75
+ Just do something like this, perhaps in your Rails initializer:
76
+
77
+ ```ruby
78
+ Holidays.between(Date.civil(2013, 1, 1), 2.years.from_now, :gb).
79
+ map{|holiday| InBusiness.holidays << holiday[:date]}
80
+ ```
81
+
82
+ This little technique is inspired by [business_time]([business_time](https://github.com/bokmann/business_time))'s readme!
83
+
84
+ ## Running specs
85
+
86
+ ```
87
+ $ bundle
88
+ $ rspec spec
89
+ ```
90
+
91
+ ## Contributing
92
+
93
+ 1. Fork it
94
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
95
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
96
+ 4. Push to the branch (`git push origin my-new-feature`)
97
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'in_business/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "in_business"
8
+ spec.version = InBusiness::VERSION
9
+ spec.summary = %Q{A gem for checking whether a given DateTime, Date or Time is within a predefined set of opening hours}
10
+ spec.authors = ["Tim Rogers"]
11
+ spec.email = ["tim@gocardless.com"]
12
+ spec.description = %q{A gem for checking whether a given DateTime, Date or Time is within a predefined set of opening hours}
13
+ spec.homepage = "https://github.com/timrogers/in_business"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "rspec", '~> 2.14.0'
21
+ spec.add_development_dependency "timecop"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_runtime_dependency "activesupport", '~> 4.0.0'
25
+ end
@@ -0,0 +1,3 @@
1
+ module InBusiness
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,69 @@
1
+ require 'ostruct'
2
+ require 'active_support/core_ext'
3
+
4
+ module InBusiness
5
+
6
+ @holidays = []
7
+ @hours = OpenStruct.new
8
+
9
+ def self.holidays
10
+ @holidays
11
+ end
12
+
13
+ def self.holidays=(array)
14
+ @holidays = array
15
+ end
16
+
17
+ def self.hours
18
+ @hours
19
+ end
20
+
21
+ def self.hours=(hash)
22
+ @hours = OpenStruct.new(hash)
23
+ end
24
+
25
+ def self.reset
26
+ # Used for clearing the state of InBusiness between specs
27
+ @holidays = []
28
+ @hours = OpenStruct.new
29
+ true
30
+ end
31
+
32
+ def self.open?(datetime)
33
+
34
+ # If this is included in the list of holidays, return false
35
+ return false if is_holiday? datetime
36
+
37
+ # If we don't know the opening hours for datetime's day, assume we're closed
38
+ return false unless hours.send(days[datetime.wday.to_s].to_sym)
39
+
40
+ # We have opening hours, so check if the current time is within them
41
+ if !hours.send(days[datetime.wday.to_s].to_sym).include? datetime.strftime("%H:%M")
42
+ return false
43
+ end
44
+
45
+ true # It's not not open, so it must be open ;)
46
+ end
47
+
48
+ def self.closed?(datetime)
49
+ !open?(datetime)
50
+ end
51
+
52
+ def self.is_holiday?(date)
53
+ @holidays.include? date.to_date
54
+ end
55
+
56
+ # Maps values of [DateTime/Date/Time]#wday to English days
57
+ def self.days
58
+ {
59
+ "0" => "sunday",
60
+ "1" => "monday",
61
+ "2" => "tuesday",
62
+ "3" => "wednesday",
63
+ "4" => "thursday",
64
+ "5" => "friday",
65
+ "6" => "saturday"
66
+ }
67
+ end
68
+
69
+ end
@@ -0,0 +1,225 @@
1
+ require 'spec_helper'
2
+
3
+ def parse_datetime_in_london(string)
4
+ ActiveSupport::TimeZone["London"].parse(string)
5
+ end
6
+
7
+ describe InBusiness do
8
+
9
+ after(:each) do
10
+ InBusiness.reset # Clear the settings so they can be set for this example
11
+ end
12
+
13
+ describe ".hours" do
14
+
15
+ it "returns an OpenStruct by default" do
16
+ InBusiness.hours.should be_a OpenStruct
17
+ end
18
+
19
+ it "can be updated from the method" do
20
+ InBusiness.hours.monday.should be_nil
21
+ InBusiness.hours.monday = "09:00".."18:00"
22
+ InBusiness.hours.monday.should eq "09:00".."18:00"
23
+ end
24
+
25
+ end
26
+
27
+ describe ".hours=" do
28
+ let(:monday_hours) { "09:00".."18:00" }
29
+ let(:options) { { monday: monday_hours } }
30
+
31
+ it "instantiates an OpenStruct with the contents of the passed hash" do
32
+ InBusiness.hours = options
33
+ InBusiness.hours.monday.should eq monday_hours
34
+ end
35
+
36
+ end
37
+
38
+ describe ".holidays" do
39
+
40
+ it "returns an empty array by default" do
41
+ InBusiness.holidays.should be_a Array
42
+ InBusiness.holidays.length.should eq 0
43
+ end
44
+
45
+ it "can be updated from the method" do
46
+ expect{InBusiness.holidays << Date.parse('1st January 2014')}.to change(InBusiness.holidays, :length).from(0).to(1)
47
+ end
48
+
49
+ end
50
+
51
+ describe ".holidays=" do
52
+ let(:holidays) do
53
+ [Date.parse('1st January 2014'), Date.parse('1st February 2014')]
54
+ end
55
+
56
+ it "sets the holidays array to the passed array" do
57
+ InBusiness.holidays = holidays
58
+ InBusiness.holidays.should eq holidays
59
+ end
60
+
61
+ end
62
+
63
+ describe ".days" do
64
+ it "returns a hash" do
65
+ InBusiness.days.should be_a Hash
66
+ end
67
+
68
+ it "contains mappings of weekday numbers to their English representation" do
69
+ InBusiness.days.fetch("1").should eq "monday"
70
+ end
71
+ end
72
+
73
+ describe ".reset" do
74
+
75
+ before do
76
+ InBusiness.holidays << Date.parse("1st January 2014")
77
+ InBusiness.hours.monday = "09:00".."23:00"
78
+ InBusiness.reset
79
+ end
80
+
81
+ it "resets .holidays to a blank array" do
82
+ InBusiness.holidays.should be_a Array
83
+ InBusiness.holidays.length.should eq 0
84
+ end
85
+
86
+ it "resets .hours to an empty OpenStruct" do
87
+ InBusiness.hours.should be_a OpenStruct
88
+ InBusiness.hours.marshall_dump.should be_nil
89
+ end
90
+
91
+ end
92
+
93
+ describe ".is_holiday?" do
94
+ let(:first_jan_2014) { Date.parse("1st January 2014") }
95
+ before { InBusiness.holidays << first_jan_2014 }
96
+
97
+ it "returns true if the provided date is a defined holiday" do
98
+ InBusiness.is_holiday?(first_jan_2014).should be_true
99
+ end
100
+
101
+ it "returns false if the provided date is not a defined holiday" do
102
+ InBusiness.is_holiday?(first_jan_2014 + 1.day).should be_false
103
+ end
104
+
105
+ end
106
+
107
+ context "with no hours set" do
108
+
109
+ describe ".open?" do
110
+
111
+ it "returns false" do
112
+ Timecop.freeze(DateTime.now) do
113
+ InBusiness.open?(DateTime.now).should be_false
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ describe ".closed?" do
120
+
121
+ it "returns true" do
122
+ Timecop.freeze(DateTime.now) do
123
+ InBusiness.closed?(DateTime.now).should be_true
124
+ end
125
+ end
126
+
127
+ end
128
+ end
129
+
130
+ context "with hours set" do
131
+ before do
132
+ InBusiness.hours.monday = "09:00".."20:00"
133
+ end
134
+
135
+ describe ".open?" do
136
+
137
+ it "returns true for a time within the hours" do
138
+ Timecop.freeze(parse_datetime_in_london("8th July 2013 12:00")) do
139
+ InBusiness.open?(DateTime.now).should be_true
140
+ end
141
+ end
142
+
143
+ it "returns false for a time outside of the hours" do
144
+ Timecop.freeze(parse_datetime_in_london("8th July 2013 20:01")) do
145
+ InBusiness.open?(DateTime.now).should be_false
146
+ end
147
+ end
148
+
149
+ it "returns false for a day that hasn't had hours set" do
150
+ Timecop.freeze(parse_datetime_in_london("9th July 2013 10:00")) do
151
+ InBusiness.open?(DateTime.now).should be_false
152
+ end
153
+ end
154
+
155
+ end
156
+
157
+ describe ".closed?" do
158
+
159
+ it "returns false for a time within the hours" do
160
+ Timecop.freeze(parse_datetime_in_london("8th July 2013 19:59")) do
161
+ InBusiness.closed?(DateTime.now).should be_false
162
+ end
163
+ end
164
+
165
+ it "returns true for a time outside of the hours" do
166
+ Timecop.freeze(parse_datetime_in_london("8th July 2013 20:01")) do
167
+ InBusiness.closed?(DateTime.now).should be_true
168
+ end
169
+ end
170
+
171
+ it "returns true for a day that hasn't had hours set" do
172
+ Timecop.freeze(parse_datetime_in_london("9th July 2013 10:00")) do
173
+ InBusiness.closed?(DateTime.now).should be_true
174
+ end
175
+ end
176
+
177
+ end
178
+
179
+ end
180
+
181
+ context "with holidays set" do
182
+
183
+ before do
184
+ InBusiness.holidays << Date.parse("8th July 2013")
185
+ InBusiness.holidays << Date.parse("1st January 2014")
186
+ InBusiness.hours.tuesday = "09:00".."11:00"
187
+ end
188
+
189
+ describe ".open?" do
190
+
191
+ it "returns false if the passed date is one of the holidays" do
192
+ Timecop.freeze(parse_datetime_in_london("8th July 2013 12:00")) do
193
+ InBusiness.open?(DateTime.now).should be_false
194
+ end
195
+ end
196
+
197
+ it "returns true if the passed date is not one of the holidays" do
198
+ Timecop.freeze(parse_datetime_in_london("9th July 2013 10:01")) do
199
+ InBusiness.open?(DateTime.now).should be_true
200
+ end
201
+ end
202
+
203
+ end
204
+
205
+ describe ".closed?" do
206
+
207
+ it "returns true if the passed date is one of the holidays" do
208
+ Timecop.freeze(parse_datetime_in_london("1st January 2014 00:00")) do
209
+ InBusiness.closed?(DateTime.now).should be_true
210
+ end
211
+ end
212
+
213
+ it "returns false if the passed date is not one of the holidays" do
214
+ Timecop.freeze(parse_datetime_in_london("9th July 2013 10:01")) do
215
+ InBusiness.closed?(DateTime.now).should be_false
216
+ end
217
+ end
218
+
219
+ end
220
+
221
+ end
222
+
223
+
224
+
225
+ end
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'rspec'
4
+ require 'timecop'
5
+ require 'in_business'
6
+
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: in_business
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tim Rogers
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.14.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.14.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: timecop
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: activesupport
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 4.0.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 4.0.0
78
+ description: A gem for checking whether a given DateTime, Date or Time is within a
79
+ predefined set of opening hours
80
+ email:
81
+ - tim@gocardless.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - .rspec
88
+ - Gemfile
89
+ - LICENSE.txt
90
+ - README.md
91
+ - Rakefile
92
+ - in_business.gemspec
93
+ - lib/in_business.rb
94
+ - lib/in_business/version.rb
95
+ - spec/in_business_spec.rb
96
+ - spec/spec_helper.rb
97
+ homepage: https://github.com/timrogers/in_business
98
+ licenses: []
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 1.8.23
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: A gem for checking whether a given DateTime, Date or Time is within a predefined
121
+ set of opening hours
122
+ test_files:
123
+ - spec/in_business_spec.rb
124
+ - spec/spec_helper.rb