time_of_day_attr 1.0.1 → 2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2e68d3a3113ec2f822baaf494be19917961776bb
4
- data.tar.gz: 55033d8269193c077a111f956e0f66c45a185eb2
3
+ metadata.gz: 3c2d0f7310864cfe4dc2da656253d4fdc657c03d
4
+ data.tar.gz: 83f2e0bd9b341836d29921fd9eb9b08218696227
5
5
  SHA512:
6
- metadata.gz: 65715383e95ff76d0b793e14d242200b42b9ef24042134619a3f3ea14e49c82ac80b483a57d73eedb7c40d9a46d31193dc777b985f724c9f8f3ba1fb92556506
7
- data.tar.gz: f013b80172f9d1260b5cfe52a9d7fbd6bff9d83468ec11f2e770faca123f856cec8ad475084af4beba9bd174a953e9fd6a573c1207dda54c31bacb681b1b49d9
6
+ metadata.gz: 8bae930ff5660092fdf4b79c6e723445982dba160fab611528c3e42699ef0c827f15d9671196085b8e9154d8b40e95393c18a4273359d685fd7f9a934897d208
7
+ data.tar.gz: 79641b79ff5de0ab50312f5871255411eb448b6eb1e5833e2c81a3beabc9c6ce059a6f91d33de33ad8666dc4d87a92bab5a255807c35de11dbced73579f3cef2
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Time of day attributes for your Rails model
2
2
  [![Gem Version](https://badge.fury.io/rb/time_of_day_attr.png)](http://badge.fury.io/rb/time_of_day_attr) [![Code Climate](https://codeclimate.com/github/clemenst/time_of_day_attr.png)](https://codeclimate.com/github/clemenst/time_of_day_attr) [![License](https://img.shields.io/npm/l/express.svg?style=flat)](http://clemenst.mit-license.org)
3
3
 
4
- This ruby gem converts time of day to seconds since midnight and back. The seconds value is stored in the database and can be used for calculations and validations.
4
+ This ruby gem converts time of day to seconds since midnight and back. The seconds value is stored in the database and can be used for calculations and validations.
5
5
 
6
6
  ## Installation
7
7
 
@@ -75,6 +75,37 @@ TimeOfDayAttr.l(business_hour.opening, format: :custom)
75
75
  => '09-00'
76
76
  ```
77
77
 
78
+ ### Prepending
79
+
80
+ If you want to process the converted value in your model, you can use the prepend option:
81
+
82
+ ```ruby
83
+ class BusinessHour < ActiveRecord::Base
84
+ attr_reader :tracked_opening, :tracked_closing
85
+
86
+ time_of_day_attr :opening
87
+ time_of_day_attr :closing, prepend: true
88
+
89
+ def opening=(value)
90
+ @tracked_opening = value
91
+ super(value)
92
+ end
93
+
94
+ def closing=(value)
95
+ @tracked_closing = value
96
+ super(value)
97
+ end
98
+ end
99
+ ```
100
+
101
+ ```ruby
102
+ business_hour = BusinessHour.new(opening: '9', closing: '9')
103
+ business_hour.tracked_opening
104
+ => '9'
105
+ business_hour.tracked_closing
106
+ => 32400
107
+ ```
108
+
78
109
  ### time of day field
79
110
 
80
111
  To get a text field with the converted value:
data/Rakefile CHANGED
@@ -20,9 +20,6 @@ RDoc::Task.new(:rdoc) do |rdoc|
20
20
  rdoc.rdoc_files.include('lib/**/*.rb')
21
21
  end
22
22
 
23
-
24
-
25
-
26
23
  Bundler::GemHelper.install_tasks
27
24
 
28
25
  require 'rake/testtask'
@@ -34,5 +31,4 @@ Rake::TestTask.new(:test) do |t|
34
31
  t.verbose = false
35
32
  end
36
33
 
37
-
38
- task :default => :test
34
+ task default: :test
@@ -1,35 +1,32 @@
1
+ require 'active_record'
2
+
1
3
  module TimeOfDayAttr
2
4
  module ActiveRecordExt
3
5
  extend ActiveSupport::Concern
4
6
 
5
7
  module ClassMethods
6
-
7
8
  def time_of_day_attr(*attrs)
8
9
  options = attrs.extract_options!
9
- options[:formats] ||= [:default, :hour]
10
- attrs.each do |attr|
11
- define_method("#{attr}=") do |value|
12
- if value.is_a?(String)
13
- delocalized_values = options[:formats].map do |format|
14
- begin
15
- TimeOfDayAttr.delocalize(value, format: format)
16
- rescue ArgumentError => e
17
- if 'argument out of range' == e.message
18
- super(nil)
19
- return
20
- end
21
- nil
22
- end
10
+
11
+ writers = Module.new do
12
+ attrs.each do |attr|
13
+ define_method("#{attr}=") do |value|
14
+ if value.is_a?(String)
15
+ delocalized_value = TimeOfDayAttr.delocalize(value, options)
16
+ super(delocalized_value)
17
+ else
18
+ super(value)
23
19
  end
24
- delocalized_value = delocalized_values.compact.first
25
- super(delocalized_value)
26
- else
27
- super(value)
28
20
  end
29
21
  end
30
22
  end
23
+
24
+ if options[:prepend]
25
+ prepend writers
26
+ else
27
+ include writers
28
+ end
31
29
  end
32
-
33
30
  end
34
31
  end
35
32
  end
@@ -1,15 +1,18 @@
1
+ require 'action_view'
2
+
1
3
  module TimeOfDayAttr
2
4
  module FormBuilderExt
3
5
  extend ActiveSupport::Concern
4
6
 
5
7
  def time_of_day_field(method, options = {})
6
- unless options[:value]
7
- value = object.send(method)
8
- options[:value] = TimeOfDayAttr.localize(value, options.extract!(:format, :omit_minutes_at_full_hour))
8
+ options[:value] ||= begin
9
+ value = object.public_send(method)
10
+ TimeOfDayAttr.localize(value, options.extract!(:format, :omit_minutes_at_full_hour))
9
11
  end
10
12
  text_field(method, options)
11
13
  end
12
-
13
14
  end
14
15
  end
15
- ActionView::Helpers::FormBuilder.send(:include, TimeOfDayAttr::FormBuilderExt)
16
+ ActiveSupport.on_load :action_view do
17
+ ActionView::Helpers::FormBuilder.send(:include, TimeOfDayAttr::FormBuilderExt)
18
+ end
@@ -0,0 +1,17 @@
1
+ module TimeOfDayAttr
2
+ Seconds = Struct.new(:value) do
3
+ def to_time_of_day(time_format)
4
+ # Switch to beginning of year to prevent wrong conversion on the day of time change
5
+ # see https://en.wikipedia.org/wiki/Daylight_saving_time
6
+ time = Time.now.beginning_of_year.at_midnight + value.seconds
7
+
8
+ time_of_day = time.strftime(time_format)
9
+
10
+ if 24.hours.to_i == value
11
+ time_of_day.gsub(' 0', '24')
12
+ else
13
+ time_of_day
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module TimeOfDayAttr
2
+ TimeOfDay = Struct.new(:value) do
3
+ def to_seconds(time_format)
4
+ time = Time.strptime(value, time_format)
5
+ # Switch to beginning of year to prevent wrong conversion on the day of time change
6
+ # see https://en.wikipedia.org/wiki/Daylight_saving_time
7
+ time = time.change(month: 1, day: 1)
8
+
9
+ seconds = time.seconds_since_midnight.to_i
10
+
11
+ if seconds.zero? && value.starts_with?('24')
12
+ 24.hours.to_i
13
+ else
14
+ seconds
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,3 @@
1
1
  module TimeOfDayAttr
2
- VERSION = '1.0.1'
2
+ VERSION = '2'.freeze
3
3
  end
@@ -1,52 +1,51 @@
1
+ require 'time_of_day_attr/time_of_day'
2
+ require 'time_of_day_attr/seconds'
1
3
  require 'time_of_day_attr/active_record_ext'
2
- require 'action_view'
3
4
  require 'time_of_day_attr/form_builder_ext'
4
5
 
5
- files = Dir[File.join(File.dirname(__FILE__), '../config/locales/*.yml')]
6
- I18n.load_path.concat(files)
6
+ formats = Dir[File.join(File.dirname(__FILE__), '../config/locales/*.yml')]
7
+ I18n.load_path.concat(formats)
7
8
 
8
9
  module TimeOfDayAttr
10
+ DEFAULT_FORMATS = [:default, :hour].freeze
9
11
 
10
12
  class << self
11
-
12
13
  def delocalize(value, options = {})
13
- format = options[:format] || :default
14
- format = translate_format(format) if format.is_a?(Symbol)
15
- time = Time.strptime(value, format).change(month: 1, day: 1)
16
- seconds = time.seconds_since_midnight.to_i
17
- if seconds.zero? && value.starts_with?('24')
18
- 24.hours.to_i
19
- else
20
- seconds
14
+ time_of_day = TimeOfDay.new(value)
15
+
16
+ formats = options[:formats] || DEFAULT_FORMATS
17
+
18
+ delocalized_values = formats.map do |format|
19
+ begin
20
+ return time_of_day.to_seconds(time_format(format))
21
+ rescue ArgumentError => e
22
+ return nil if e.message.include?('out of range')
23
+ next
24
+ end
21
25
  end
26
+
27
+ delocalized_values.compact.first
22
28
  end
23
29
 
24
30
  def localize(value, options = {})
25
31
  return value unless value.respond_to?(:seconds)
26
- format = options[:format] || :default
27
- format = translate_format(format) if format.is_a?(Symbol)
28
- # Switch to beginning of year to prevent wrong time on the day of time change
29
- # see https://en.wikipedia.org/wiki/Daylight_saving_time
30
- time = Time.now.beginning_of_year.at_midnight + value.seconds
31
- time_of_day = time.strftime(format)
32
- if 24.hours.to_i == value
33
- time_of_day.gsub!(' 0', '24')
34
- end
35
- if options[:omit_minutes_at_full_hour]
36
- if time_of_day.end_with?('00')
37
- time_of_day = time_of_day[0...-3]
38
- end
39
- end
40
- time_of_day
32
+
33
+ format = options[:format] || DEFAULT_FORMATS.first
34
+
35
+ time_of_day = Seconds.new(value).to_time_of_day(time_format(format))
36
+
37
+ omit_minutes = options[:omit_minutes_at_full_hour] && time_of_day.end_with?('00')
38
+
39
+ omit_minutes ? time_of_day[0...-3] : time_of_day
41
40
  end
42
- alias :l :localize
41
+ alias l localize
43
42
 
44
43
  private
45
44
 
46
- def translate_format(format)
47
- I18n.translate("time_of_day.formats.#{format}")
48
- end
45
+ def time_format(format)
46
+ translate = format.is_a?(Symbol)
49
47
 
48
+ translate ? I18n.translate("time_of_day.formats.#{format}") : format
49
+ end
50
50
  end
51
-
52
51
  end
@@ -0,0 +1,64 @@
1
+ require 'test_helper'
2
+ require 'active_record'
3
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
4
+ require_relative 'schema'
5
+ require_relative 'models/business_hour'
6
+
7
+ class ActiveRecordExtTest < ActiveSupport::TestCase
8
+ context 'time of day value' do
9
+ setup do
10
+ @business_hour = BusinessHour.new(opening: '9:00', closing: '17:00')
11
+ end
12
+
13
+ should 'be converted to seconds since midnight' do
14
+ assert_equal 32_400, @business_hour.opening
15
+ assert_equal 61_200, @business_hour.closing
16
+ end
17
+ end
18
+
19
+ context 'hour formatted time of day value' do
20
+ setup do
21
+ @business_hour = BusinessHour.new(opening: '9', closing: '17')
22
+ end
23
+
24
+ should 'be converted to seconds since midnight' do
25
+ assert_equal 32_400, @business_hour.opening
26
+ assert_equal 61_200, @business_hour.closing
27
+ end
28
+ end
29
+
30
+ context 'unsupported time of day value' do
31
+ setup do
32
+ @business_hour = BusinessHour.new(opening: 55_800)
33
+ @business_hour.opening = 'Nine'
34
+ end
35
+
36
+ should 'be converted to nil' do
37
+ assert_nil @business_hour.opening
38
+ end
39
+ end
40
+
41
+ context 'non string value' do
42
+ setup do
43
+ @business_hour = BusinessHour.new(opening: 55_800)
44
+ end
45
+
46
+ should 'not be converted' do
47
+ assert_equal 55_800, @business_hour.opening
48
+ end
49
+ end
50
+
51
+ context 'prepend option' do
52
+ setup do
53
+ @business_hour = BusinessHour.new(opening: '9', closing: '9')
54
+ end
55
+
56
+ should 'be supported' do
57
+ assert_equal '9', @business_hour.tracked_opening
58
+ assert_equal 32_400, @business_hour.opening
59
+
60
+ assert_equal 32_400, @business_hour.tracked_closing
61
+ assert_equal 32_400, @business_hour.closing
62
+ end
63
+ end
64
+ end
@@ -1,3 +1,16 @@
1
1
  class BusinessHour < ActiveRecord::Base
2
- time_of_day_attr :opening, :closing
2
+ attr_reader :tracked_opening, :tracked_closing
3
+
4
+ time_of_day_attr :opening
5
+ time_of_day_attr :closing, prepend: true
6
+
7
+ def opening=(value)
8
+ @tracked_opening = value
9
+ super(value)
10
+ end
11
+
12
+ def closing=(value)
13
+ @tracked_closing = value
14
+ super(value)
15
+ end
3
16
  end
data/test/schema.rb CHANGED
@@ -1,8 +1,7 @@
1
1
  ActiveRecord::Migration.verbose = false
2
2
 
3
3
  ActiveRecord::Schema.define do
4
-
5
- create_table :business_hours, :force => true do |t|
4
+ create_table :business_hours, force: true do |t|
6
5
  t.integer :opening
7
6
  t.integer :closing
8
7
  end
data/test/test_helper.rb CHANGED
@@ -1,12 +1,5 @@
1
- # Configure Rails Environment
2
- ENV["RAILS_ENV"] = "test"
1
+ ENV['RAILS_ENV'] = 'test'
3
2
 
4
- require 'active_record'
5
3
  require 'minitest/autorun'
4
+ require 'shoulda/context'
6
5
  require 'time_of_day_attr'
7
-
8
- ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
9
-
10
- require File.expand_path('../schema', __FILE__)
11
-
12
- Dir["#{File.dirname(__FILE__)}/models/**/*.rb"].each { |f| require f }
@@ -1,71 +1,43 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class TimeOfDayAttrTest < ActiveSupport::TestCase
4
-
5
- test 'delocalize time of day' do
6
- assert_equal 36000, TimeOfDayAttr.delocalize('10:00')
7
- assert_equal 55800, TimeOfDayAttr.delocalize('15:30')
8
- assert_equal 61140, TimeOfDayAttr.delocalize('16:59')
9
- assert_equal 32400, TimeOfDayAttr.delocalize('9:00')
10
- assert_equal 32400, TimeOfDayAttr.delocalize('09:00')
11
- assert_equal 32400, TimeOfDayAttr.delocalize('9', format: :hour)
4
+ context '::localize' do
5
+ should 'convert seconds to time of day' do
6
+ assert_equal ' 9:00', TimeOfDayAttr.localize(32_400)
7
+ assert_equal '14:45', TimeOfDayAttr.localize(53_100)
8
+ end
9
+
10
+ should 'omit minutes at full hour' do
11
+ assert_equal ' 9', TimeOfDayAttr.localize(32_400, omit_minutes_at_full_hour: true)
12
+ assert_equal '14', TimeOfDayAttr.localize(50_400, omit_minutes_at_full_hour: true)
13
+ assert_equal '14:45', TimeOfDayAttr.localize(53_100, omit_minutes_at_full_hour: true)
14
+ end
15
+
16
+ should 'convert nil to nil' do
17
+ assert_nil TimeOfDayAttr.localize(nil)
18
+ end
19
+
20
+ should 'support 24 hours' do
21
+ assert_equal ' 0:00', TimeOfDayAttr.localize(0)
22
+ assert_equal '24:00', TimeOfDayAttr.localize(86_400)
23
+ assert_equal ' 0', TimeOfDayAttr.localize(0, omit_minutes_at_full_hour: true)
24
+ assert_equal '24', TimeOfDayAttr.localize(86_400, omit_minutes_at_full_hour: true)
25
+ end
26
+ end
27
+
28
+ context '::delocaize' do
29
+ should 'convert time of day to seconds' do
30
+ assert_equal 36_000, TimeOfDayAttr.delocalize('10:00')
31
+ assert_equal 55_800, TimeOfDayAttr.delocalize('15:30')
32
+ assert_equal 61_140, TimeOfDayAttr.delocalize('16:59')
33
+ assert_equal 32_400, TimeOfDayAttr.delocalize('9:00')
34
+ assert_equal 32_400, TimeOfDayAttr.delocalize('09:00')
35
+ assert_equal 32_400, TimeOfDayAttr.delocalize('9', formats: [:hour])
36
+ end
37
+
38
+ should 'convert out of range time of day to nil' do
39
+ assert_nil TimeOfDayAttr.delocalize('25:00')
40
+ assert_nil TimeOfDayAttr.delocalize('24:30')
41
+ end
12
42
  end
13
-
14
- test 'localize time of day' do
15
- assert_equal ' 9:00', TimeOfDayAttr.localize(32400)
16
- assert_equal '14:45', TimeOfDayAttr.localize(53100)
17
- end
18
-
19
- test 'localize time of day and omit minutes at full hour' do
20
- assert_equal ' 9', TimeOfDayAttr.localize(32400, omit_minutes_at_full_hour: true)
21
- assert_equal '14', TimeOfDayAttr.localize(50400, omit_minutes_at_full_hour: true)
22
- assert_equal '14:45', TimeOfDayAttr.localize(53100, omit_minutes_at_full_hour: true)
23
- end
24
-
25
- test 'localizing nil should return nil' do
26
- assert_nil TimeOfDayAttr.localize(nil)
27
- end
28
-
29
- test 'time of day attr setter should delocalize value' do
30
- business_hour = BusinessHour.new(opening: '9:00', closing: '17:00')
31
- assert_equal 32400, business_hour.opening
32
- assert_equal 61200, business_hour.closing
33
- end
34
-
35
- test 'time of day attr setter should delocalize hour formatted value' do
36
- business_hour = BusinessHour.new(opening: '9', closing: '17')
37
- assert_equal 32400, business_hour.opening
38
- assert_equal 61200, business_hour.closing
39
- end
40
-
41
- test 'time of day attr setter should ignore invalid formats' do
42
- business_hour = BusinessHour.new(opening: 'Nine', closing: 'Five')
43
- assert_nil business_hour.opening
44
- assert_nil business_hour.closing
45
- business_hour.opening = '9'
46
- assert_equal 32400, business_hour.opening
47
- business_hour.opening = 'Nine'
48
- assert_nil business_hour.opening
49
- business_hour.opening = '9:30'
50
- assert_equal 34200, business_hour.opening
51
- business_hour.opening = 55800
52
- assert_equal 55800, business_hour.opening
53
- business_hour.opening = nil
54
- assert_nil business_hour.opening
55
- business_hour.opening = '25:00'
56
- assert_nil business_hour.opening
57
- business_hour.opening = '24:30'
58
- assert_nil business_hour.opening
59
- end
60
-
61
- test '24 should be usable' do
62
- business_hour = BusinessHour.new(opening: '0', closing: '24')
63
- assert_equal 0, business_hour.opening
64
- assert_equal 86400, business_hour.closing
65
- assert_equal ' 0:00', TimeOfDayAttr.localize(business_hour.opening)
66
- assert_equal '24:00', TimeOfDayAttr.localize(business_hour.closing)
67
- assert_equal ' 0', TimeOfDayAttr.localize(business_hour.opening, omit_minutes_at_full_hour: true)
68
- assert_equal '24', TimeOfDayAttr.localize(business_hour.closing, omit_minutes_at_full_hour: true)
69
- end
70
-
71
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: time_of_day_attr
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: '2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clemens Teichmann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-03 00:00:00.000000000 Z
11
+ date: 2017-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -54,13 +54,17 @@ files:
54
54
  - lib/time_of_day_attr.rb
55
55
  - lib/time_of_day_attr/active_record_ext.rb
56
56
  - lib/time_of_day_attr/form_builder_ext.rb
57
+ - lib/time_of_day_attr/seconds.rb
58
+ - lib/time_of_day_attr/time_of_day.rb
57
59
  - lib/time_of_day_attr/version.rb
60
+ - test/active_record_ext_test.rb
58
61
  - test/models/business_hour.rb
59
62
  - test/schema.rb
60
63
  - test/test_helper.rb
61
64
  - test/time_of_day_attr_test.rb
62
65
  homepage: https://github.com/clemenst/time_of_day_attr
63
- licenses: []
66
+ licenses:
67
+ - MIT
64
68
  metadata: {}
65
69
  post_install_message:
66
70
  rdoc_options: []
@@ -78,12 +82,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
82
  version: '0'
79
83
  requirements: []
80
84
  rubyforge_project:
81
- rubygems_version: 2.5.2
85
+ rubygems_version: 2.6.8
82
86
  signing_key:
83
87
  specification_version: 4
84
88
  summary: Time of day attributes for your Rails model
85
89
  test_files:
86
90
  - test/schema.rb
87
91
  - test/models/business_hour.rb
92
+ - test/active_record_ext_test.rb
88
93
  - test/time_of_day_attr_test.rb
89
94
  - test/test_helper.rb