in_business 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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