split_dmy 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +145 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +67 -0
- data/Rakefile +8 -0
- data/lib/split_dmy.rb +3 -0
- data/lib/split_dmy/date_validator.rb +123 -0
- data/lib/split_dmy/split_accessors.rb +67 -0
- data/lib/split_dmy/version.rb +3 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/split_dmy/split_accessors_spec.rb +343 -0
- data/spec/support/matchers/accessors_shared.rb +18 -0
- data/spec/support/models.rb +2 -0
- data/spec/support/schema.rb +9 -0
- data/split_dmy.gemspec +35 -0
- metadata +203 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 31735b92deac70f49b3d910445e504f16d818a62
|
4
|
+
data.tar.gz: 19083a589f95add2d3e1610c1d8ea6ba8e60f4d0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8b30f8b20a42374ddef4a4e3ca84bf7b8e93a373d24ef125df141463d0f0ef22a096f829975268dde58e5be1fbe2a781875fa26a89152a0ab9a877629bef575f
|
7
|
+
data.tar.gz: 98a9addb8fc1f2855c8595e5c20f586f60a5d577ac1aa3b46959e3a153696b1ca53f258bba06f5d24d8c60690198155d9efa8baa222e679cc07d95500f831b4a
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
AllCops:
|
2
|
+
# Exclude anything that isn't really part of our code.
|
3
|
+
# rails_helper is excluded because it's full of solecisms, but it's mostly
|
4
|
+
# generated code and copy-and-pasted snipets from READMEs.
|
5
|
+
Exclude:
|
6
|
+
- 'Guardfile'
|
7
|
+
- 'vendor/**/*'
|
8
|
+
- 'db/**/*'
|
9
|
+
- 'bin/**/*'
|
10
|
+
- '**/*.gemspec'
|
11
|
+
- '**/Rakefile'
|
12
|
+
- '**/Vagrantfile'
|
13
|
+
- 'spec/rails_helper.rb'
|
14
|
+
- 'lib/tasks/cucumber.rake'
|
15
|
+
- 'features/support/env.rb'
|
16
|
+
- 'script/cucumber'
|
17
|
+
- 'lib/log_stuff.rb'
|
18
|
+
|
19
|
+
RunRailsCops: true
|
20
|
+
|
21
|
+
# Allow long lines in specs, as it's almost impossible to fit RSpec's
|
22
|
+
# expectations into 80 characters.
|
23
|
+
Metrics/LineLength:
|
24
|
+
Max: 80
|
25
|
+
Exclude:
|
26
|
+
- 'spec/**/*'
|
27
|
+
|
28
|
+
# Allow expect {}.to blocks in specs
|
29
|
+
# but not in the code
|
30
|
+
Style/BlockDelimiters:
|
31
|
+
Enabled: true
|
32
|
+
Exclude:
|
33
|
+
- 'spec/**/*'
|
34
|
+
|
35
|
+
# Don't worry about long methods in specs.
|
36
|
+
Metrics/MethodLength:
|
37
|
+
Exclude:
|
38
|
+
- 'spec/**/*'
|
39
|
+
- 'app/models/ability.rb'
|
40
|
+
- 'config/initializers/markdown_handler.rb'
|
41
|
+
|
42
|
+
# No need to check for describe class param in support files.
|
43
|
+
RSpec/DescribeClass:
|
44
|
+
Exclude:
|
45
|
+
- spec/support/**/*
|
46
|
+
- spec/views/**/*
|
47
|
+
|
48
|
+
RSpec/FilePath:
|
49
|
+
Exclude:
|
50
|
+
- spec/routing/**/*
|
51
|
+
|
52
|
+
# private/protected/public
|
53
|
+
Style/AccessModifierIndentation:
|
54
|
+
EnforcedStyle: indent
|
55
|
+
|
56
|
+
# Just indent parameters by two spaces. It's less volatile if methods change,
|
57
|
+
# and there's less busy work lining things up.
|
58
|
+
Style/AlignParameters:
|
59
|
+
EnforcedStyle: with_fixed_indentation
|
60
|
+
|
61
|
+
Style/ClassAndModuleChildren:
|
62
|
+
EnforcedStyle: nested
|
63
|
+
Exclude:
|
64
|
+
- app/controllers/users/invitations_controller.rb
|
65
|
+
|
66
|
+
Style/CollectionMethods:
|
67
|
+
PreferredMethods:
|
68
|
+
collect: 'map'
|
69
|
+
collect!: 'map!'
|
70
|
+
each_with_object: 'inject'
|
71
|
+
inject: 'inject'
|
72
|
+
reduce: 'inject'
|
73
|
+
detect: 'find'
|
74
|
+
find_all: 'select'
|
75
|
+
|
76
|
+
# Chain methods with trailing dots.
|
77
|
+
Style/DotPosition:
|
78
|
+
EnforcedStyle: trailing
|
79
|
+
|
80
|
+
# No, we don't prefer each_with_object
|
81
|
+
Style/EachWithObject:
|
82
|
+
Enabled: false
|
83
|
+
|
84
|
+
# Prefer blank line after class/module start.
|
85
|
+
Style/EmptyLinesAroundClassBody:
|
86
|
+
Enabled: false
|
87
|
+
|
88
|
+
Style/EmptyLinesAroundModuleBody:
|
89
|
+
Enabled: false
|
90
|
+
|
91
|
+
Style/EmptyLinesAroundBlockBody:
|
92
|
+
Enabled: false
|
93
|
+
|
94
|
+
# We have a mixture at the moment, so don't enforce anything.
|
95
|
+
Style/FormatString:
|
96
|
+
Enabled: false
|
97
|
+
|
98
|
+
# It's not really clearer to replace every if with a return if.
|
99
|
+
Style/GuardClause:
|
100
|
+
Enabled: false
|
101
|
+
|
102
|
+
# Groups of three is not always the right thing for numeric literals
|
103
|
+
Style/NumericLiterals:
|
104
|
+
Enabled: false
|
105
|
+
|
106
|
+
# Percent-formatting and hash interpolation both have their place. Don't
|
107
|
+
# enforce any particular one.
|
108
|
+
Style/StringLiterals:
|
109
|
+
Enabled: false
|
110
|
+
|
111
|
+
# I'm happy with raise, thanks.
|
112
|
+
Style/SignalException:
|
113
|
+
Enabled: false
|
114
|
+
|
115
|
+
# Let us use foo? methods
|
116
|
+
Style/TrivialAccessors:
|
117
|
+
AllowPredicates: true
|
118
|
+
|
119
|
+
# Prefer sensible naming to comments everywhere.
|
120
|
+
Documentation:
|
121
|
+
Description: Document classes and non-namespace modules.
|
122
|
+
Enabled: false
|
123
|
+
|
124
|
+
# Would enforce do_y if x over if x / do y / end. As with GuardClause above,
|
125
|
+
# this enforces code organisation that doesn't necesarily make things clearer.
|
126
|
+
IfUnlessModifier:
|
127
|
+
Enabled: false
|
128
|
+
|
129
|
+
# Allow safe assignment in conditions.
|
130
|
+
Lint/AssignmentInCondition:
|
131
|
+
AllowSafeAssignment: false
|
132
|
+
|
133
|
+
# Just a preference to use %w[] over %w()
|
134
|
+
Style/PercentLiteralDelimiters:
|
135
|
+
PreferredDelimiters:
|
136
|
+
'%w': '[]'
|
137
|
+
'%W': '[]'
|
138
|
+
'%i': '[]'
|
139
|
+
|
140
|
+
# %w doesn't always make for clearer test data
|
141
|
+
Style/WordArray:
|
142
|
+
Exclude:
|
143
|
+
- 'spec/**/*'
|
144
|
+
|
145
|
+
require: rubocop-rspec
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Colin Bruce
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# SplitDmy
|
2
|
+
[](https://travis-ci.org/CeeBeeUK/split_dmy)
|
3
|
+
[](https://codeclimate.com/github/CeeBeeUK/split_dmy)
|
4
|
+
[](https://codeclimate.com/github/CeeBeeUK/split_dmy/coverage)
|
5
|
+
|
6
|
+
[](https://hakiri.io/github/CeeBeeUK/split_dmy/master)
|
7
|
+
[](http://waffle.io/CeeBeeUK/split_dmy)
|
8
|
+
|
9
|
+
Allow splitting a date field into constituent day, month and year parts.
|
10
|
+
|
11
|
+
Splitting dates into constituent parts is recommended by the GOV.UK
|
12
|
+
[service manual](https://www.gov.uk/service-manual/user-centred-design/resources/patterns/dates.html#memorable-dates)
|
13
|
+
this gem is designed to allow a simple method of displaying the split date on a view, without having
|
14
|
+
to manually de and re-compose the date in the controller.
|
15
|
+
|
16
|
+
This gem was inspired by [TimeSplitter](https://github.com/shekibobo/time_splitter)
|
17
|
+
by [shekibobo](https://github.com/shekibobo), in turn based on
|
18
|
+
[SplitDatetime](https://github.com/michihuber/split_datetime)
|
19
|
+
by [Michi Huber](https://github.com/michihuber).
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'split_dmy'
|
27
|
+
```
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
$ bundle
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install split_dmy
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
After bundling, assuming you have a person model with a date_of_birth attribute, add this to your model:
|
40
|
+
```ruby
|
41
|
+
class Person < ActiveRecord::Base
|
42
|
+
extend SplitDmy::Accessors
|
43
|
+
split_dmy_accessor :date_of_birth
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
In your view (if using slim):
|
48
|
+
```ruby
|
49
|
+
= form_for(@person) do |f|
|
50
|
+
= f.text_field :date_of_birth_day
|
51
|
+
= f.text_field :date_of_birth_month
|
52
|
+
= f.text_field :date_of_birth_year
|
53
|
+
```
|
54
|
+
|
55
|
+
In your controller, add the new variables to the strong parameter list
|
56
|
+
```ruby
|
57
|
+
params.require(:person).permit(:name, :date_of_birth_day, :date_of_birth_month, :date_of_birth_year)
|
58
|
+
```
|
59
|
+
|
60
|
+
|
61
|
+
## Contributing
|
62
|
+
|
63
|
+
1. Fork it ( https://github.com/ceebeeuk/split-date-dmy/fork )
|
64
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
65
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
66
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
67
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/lib/split_dmy.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
module SplitDmy
|
2
|
+
class DateValidator
|
3
|
+
def initialize(object, attribute)
|
4
|
+
@split_day = object.instance_variable_get("@#{attribute}_day")
|
5
|
+
@split_month = object.instance_variable_get("@#{attribute}_month")
|
6
|
+
@split_year = object.instance_variable_get("@#{attribute}_year").to_i
|
7
|
+
@object = object
|
8
|
+
@attr = attribute
|
9
|
+
end
|
10
|
+
|
11
|
+
def partial_updated
|
12
|
+
bd = build_date
|
13
|
+
partials_valid? && bd ? bd : nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse_month(val)
|
17
|
+
if valid_fixnum?(val, 12) || valid_numeric_string?(val, 12)
|
18
|
+
result = val.to_i
|
19
|
+
else
|
20
|
+
mon_name = valid_month_name?(val)
|
21
|
+
result = mon_name.present? ? mon_name : val
|
22
|
+
end
|
23
|
+
result.to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_partial_error(part)
|
27
|
+
error = "#{part} must be completed" if "@split_#{part}".to_s.empty?
|
28
|
+
error = "is not a valid #{part}" unless send("valid_#{part}?")
|
29
|
+
error
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_date
|
33
|
+
date = Date.new(
|
34
|
+
@split_year.to_i,
|
35
|
+
parse_month(@split_month),
|
36
|
+
@split_day.to_i
|
37
|
+
)
|
38
|
+
date if partials_match_date(date)
|
39
|
+
rescue
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate_errors
|
44
|
+
%w[day month year].each do |part|
|
45
|
+
error = get_partial_error(part)
|
46
|
+
@object.errors.add("#{@attr}_#{part}".to_sym, error) if error.present?
|
47
|
+
end
|
48
|
+
|
49
|
+
err = []
|
50
|
+
err << 'you need to provide a valid date' if all_partials_empty?
|
51
|
+
err << combine_partials_error if partials_valid_date_fails?
|
52
|
+
err
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def partials_valid?
|
58
|
+
{ d: valid_day?, m: valid_month?, y: valid_year? }.values.all?
|
59
|
+
end
|
60
|
+
|
61
|
+
def partials_valid_date_fails?
|
62
|
+
partials_valid? && !build_date
|
63
|
+
end
|
64
|
+
|
65
|
+
def combine_partials_error
|
66
|
+
joined_date = [@split_day, @split_month, @split_year].join('-')
|
67
|
+
"'#{joined_date}' is not a valid date"
|
68
|
+
end
|
69
|
+
|
70
|
+
def all_partials_empty?
|
71
|
+
[@split_day.empty?, @split_month.empty?, @split_year.empty?].values.all?
|
72
|
+
rescue
|
73
|
+
false
|
74
|
+
end
|
75
|
+
|
76
|
+
def partials_match_date(date)
|
77
|
+
date.day == @split_day.to_i &&
|
78
|
+
date.month == parse_month(@split_month).to_i &&
|
79
|
+
date.year == @split_year.to_i
|
80
|
+
end
|
81
|
+
|
82
|
+
def valid_day?
|
83
|
+
(valid_fixnum?(@split_day, 31) || valid_numeric_string?(@split_day, 31))
|
84
|
+
end
|
85
|
+
|
86
|
+
def valid_month?
|
87
|
+
(
|
88
|
+
valid_fixnum?(@split_month, 12) ||
|
89
|
+
valid_numeric_string?(@split_month, 12) ||
|
90
|
+
valid_month_name?(@split_month)
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
def valid_year?
|
95
|
+
(
|
96
|
+
valid_fixnum?(@split_year, 3333) ||
|
97
|
+
valid_numeric_string_year?(@split_year)
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
def valid_fixnum?(x, max)
|
102
|
+
x.is_a?(Fixnum) && x > 0 && x <= max
|
103
|
+
end
|
104
|
+
|
105
|
+
def valid_numeric_string?(x, max)
|
106
|
+
x =~ /^[0-9]{1,2}$/ && x.to_i <= max
|
107
|
+
end
|
108
|
+
|
109
|
+
def valid_numeric_string_year?(x)
|
110
|
+
x =~ /^[0-9]{4}$/ && x.to_i <= 3333 && x.to_i > 0000
|
111
|
+
end
|
112
|
+
|
113
|
+
def valid_month_name?(month)
|
114
|
+
short_months = I18n.t('date.abbr_month_names')
|
115
|
+
full_months = I18n.t('date.month_names')
|
116
|
+
if short_months.include?(month.to_s.capitalize)
|
117
|
+
short_months.index(month.capitalize)
|
118
|
+
elsif full_months.include?(month.to_s.capitalize)
|
119
|
+
full_months.index(month.capitalize)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module SplitDmy
|
2
|
+
module SplitAccessors
|
3
|
+
def split_dmy_accessor(*attrs)
|
4
|
+
require 'split_dmy/date_validator'
|
5
|
+
|
6
|
+
attrs.each do |attr|
|
7
|
+
validate "validate_#{attr}_partials".to_sym
|
8
|
+
|
9
|
+
override_builtin(attr)
|
10
|
+
add_attr_accessors(attr)
|
11
|
+
extend_validation(attr)
|
12
|
+
end
|
13
|
+
add_methods
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def extend_validation(attr)
|
19
|
+
define_method("validate_#{attr}_partials") do
|
20
|
+
dv = DateValidator.new(self, attr)
|
21
|
+
new_errs = dv.generate_errors
|
22
|
+
unless new_errs.empty?
|
23
|
+
errors.delete(attr.to_sym)
|
24
|
+
errors.add(attr.to_sym, "is not valid, #{make_sentence_of(new_errs)}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def override_builtin(attr)
|
30
|
+
after_initialize do
|
31
|
+
full_date = send("#{attr}")
|
32
|
+
split_into_parts(attr, full_date) unless full_date.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
define_method("#{attr}=") do |val|
|
36
|
+
super(val)
|
37
|
+
split_into_parts(attr, Date.parse(val.to_s)) unless val.nil?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_methods
|
42
|
+
define_method('split_into_parts') do |attr, full_date|
|
43
|
+
instance_variable_set("@#{attr}_day", full_date.day)
|
44
|
+
instance_variable_set("@#{attr}_month", full_date.month)
|
45
|
+
instance_variable_set("@#{attr}_year", full_date.year)
|
46
|
+
end
|
47
|
+
|
48
|
+
define_method('make_sentence_of') do |errors|
|
49
|
+
errors.to_sentence(last_word_connector: ' and ')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_attr_accessors(attr)
|
54
|
+
%w[day month year].each do |part|
|
55
|
+
define_method("#{attr}_#{part}=") do |val|
|
56
|
+
instance_variable_set("@#{attr}_#{part}", val)
|
57
|
+
new = DateValidator.new(self, attr).partial_updated
|
58
|
+
send("#{attr}=", new)
|
59
|
+
end
|
60
|
+
|
61
|
+
define_method("#{attr}_#{part}") do
|
62
|
+
instance_variable_get("@#{attr}_#{part}")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
if ENV['CODECLIMATE_REPO_TOKEN']
|
2
|
+
# allow Code Climate Test coverage reports to be sent
|
3
|
+
require 'codeclimate-test-reporter'
|
4
|
+
CodeClimate::TestReporter.start
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'active_record'
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
10
|
+
|
11
|
+
load File.dirname(__FILE__) + '/support/schema.rb'
|
12
|
+
require File.dirname(__FILE__) + '/support/models.rb'
|
@@ -0,0 +1,343 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'split_dmy/split_accessors'
|
3
|
+
require_relative '../../spec/support/matchers/accessors_shared'
|
4
|
+
|
5
|
+
describe SplitDmy::SplitAccessors do
|
6
|
+
let(:model) { User.new }
|
7
|
+
|
8
|
+
describe 'test model' do
|
9
|
+
it 'has a date of birth field to extend' do
|
10
|
+
expect(model).to respond_to :date_of_birth
|
11
|
+
end
|
12
|
+
it 'responds to date_of_birth' do
|
13
|
+
expect(model).to have_attr_accessor :date_of_birth
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'after adding our gem, a model' do
|
18
|
+
before do
|
19
|
+
User.extend(described_class)
|
20
|
+
User.split_dmy_accessor(:date_of_birth)
|
21
|
+
end
|
22
|
+
|
23
|
+
['day', 'month', 'year'].each do |part|
|
24
|
+
it "responds to attribute_#{part}" do
|
25
|
+
expect(model).to respond_to "date_of_birth_#{part}".to_sym
|
26
|
+
end
|
27
|
+
|
28
|
+
it "has split accessor for attribute_#{part}" do
|
29
|
+
expect(model).to have_attr_accessor "date_of_birth_#{part}".to_sym
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'split dmy methods' do
|
34
|
+
describe 'handles pre-set dates' do
|
35
|
+
describe 'passes them to the new accessors' do
|
36
|
+
before(:each) { model.date_of_birth = '1991-1-21' }
|
37
|
+
|
38
|
+
it 'sets the _day' do
|
39
|
+
expect(model.date_of_birth_day).to eq 21
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when object created with date' do
|
44
|
+
let(:model) { User.new(date_of_birth: Date.new(1992, 1, 21)) }
|
45
|
+
|
46
|
+
it 'sets the date' do
|
47
|
+
expect(model.date_of_birth).to eq Date.new(1992, 1, 21)
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'splits the partials' do
|
51
|
+
it 'sets the day' do
|
52
|
+
expect(model.date_of_birth_day).to eq 21
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'sets the month' do
|
56
|
+
expect(model.date_of_birth_month).to eq 1
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'sets the year' do
|
60
|
+
expect(model.date_of_birth_year).to eq 1992
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'allows clearing of preset data' do
|
66
|
+
before(:each) { model.date_of_birth = '1991-1-21' }
|
67
|
+
|
68
|
+
it 'by setting all values to nil' do
|
69
|
+
model.date_of_birth_day = nil
|
70
|
+
model.date_of_birth_month = nil
|
71
|
+
model.date_of_birth_year = nil
|
72
|
+
expect(model.date_of_birth).to be_nil
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'by setting' do
|
76
|
+
it 'day to nil' do
|
77
|
+
model.date_of_birth_day = nil
|
78
|
+
expect(model.date_of_birth).to be_nil
|
79
|
+
end
|
80
|
+
it 'month to nil' do
|
81
|
+
model.date_of_birth_month = nil
|
82
|
+
expect(model.date_of_birth).to be_nil
|
83
|
+
end
|
84
|
+
it 'year to nil' do
|
85
|
+
model.date_of_birth_year = nil
|
86
|
+
expect(model.date_of_birth).to be_nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '_day' do
|
93
|
+
describe 'when sent numbers' do
|
94
|
+
describe 'between 1 and 31 is valid' do
|
95
|
+
it 'sets the value' do
|
96
|
+
(1..31).each do |i|
|
97
|
+
model.date_of_birth_day = i
|
98
|
+
expect(model.date_of_birth_day).to eq i
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'above accepted range' do
|
104
|
+
before do
|
105
|
+
model.date_of_birth_day = 32
|
106
|
+
model.valid?
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'sets the value' do
|
110
|
+
expect(model.date_of_birth_day).to eq 32
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'adds an error message' do
|
114
|
+
err_msg = ['is not a valid day']
|
115
|
+
expect(model.errors[:date_of_birth_day]).to eq err_msg
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe 'below accepted range' do
|
120
|
+
before { model.date_of_birth_day = 0 }
|
121
|
+
|
122
|
+
it 'sets the value' do
|
123
|
+
expect(model.date_of_birth_day).to eq 0
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'makes the model invalid' do
|
127
|
+
expect(model).to be_invalid
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe 'when sent text' do
|
133
|
+
before { model.date_of_birth_day = 'first' }
|
134
|
+
|
135
|
+
it 'returns nil' do
|
136
|
+
expect(model.date_of_birth_day).to eq 'first'
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'makes the model invalid' do
|
140
|
+
expect(model).to be_invalid
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe 'when sent nil' do
|
145
|
+
it 'returns nil' do
|
146
|
+
model.date_of_birth_day = nil
|
147
|
+
expect(model.date_of_birth_day).to be_nil
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe '_month' do
|
153
|
+
describe 'when sent numbers' do
|
154
|
+
describe 'between 1 and 31 is valid' do
|
155
|
+
it 'sets the value' do
|
156
|
+
(1..12).each do |i|
|
157
|
+
model.date_of_birth_month = i
|
158
|
+
expect(model.date_of_birth_month).to eq i
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe 'above accepted range' do
|
164
|
+
before { model.date_of_birth_month = 13 }
|
165
|
+
|
166
|
+
it 'sets the value' do
|
167
|
+
expect(model.date_of_birth_month).to eq 13
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'makes the model invalid' do
|
171
|
+
expect(model).to be_invalid
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe 'below accepted range' do
|
176
|
+
before { model.date_of_birth_month = 0 }
|
177
|
+
|
178
|
+
it 'sets the value' do
|
179
|
+
expect(model.date_of_birth_month).to eq 0
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'makes the model invalid' do
|
183
|
+
expect(model).to be_invalid
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe 'when sent text' do
|
189
|
+
describe 'that is a valid long month' do
|
190
|
+
I18n.t('date.month_names').each_with_index do |month, _index|
|
191
|
+
it "sending #{month} sets the month to #{month}" do
|
192
|
+
model.date_of_birth_month = month
|
193
|
+
expect(model.date_of_birth_month).to eq month unless month.nil?
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
describe 'that is a valid short month' do
|
198
|
+
I18n.t('date.abbr_month_names').each_with_index do |month, _index|
|
199
|
+
it "sending #{month} sets the month to #{month}" do
|
200
|
+
model.date_of_birth_month = month
|
201
|
+
expect(model.date_of_birth_month).to eq month unless month.nil?
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
describe 'that is not a valid month' do
|
206
|
+
before { model.date_of_birth_month = 'first' }
|
207
|
+
|
208
|
+
it 'returns the text' do
|
209
|
+
expect(model.date_of_birth_month).to eq 'first'
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'makes the model invalid' do
|
213
|
+
expect(model).to be_invalid
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe 'when sent nil' do
|
219
|
+
it 'returns nil' do
|
220
|
+
model.date_of_birth_month = nil
|
221
|
+
expect(model.date_of_birth_month).to be_nil
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe '_year' do
|
227
|
+
describe 'when sent numbers' do
|
228
|
+
describe 'numerically' do
|
229
|
+
describe 'in the acceptable range' do
|
230
|
+
it 'sets the value' do
|
231
|
+
model.date_of_birth_year = 1961
|
232
|
+
expect(model.date_of_birth_year).to eq 1961
|
233
|
+
end
|
234
|
+
end
|
235
|
+
describe 'outside the acceptable range' do
|
236
|
+
before { model.date_of_birth_year = 3334 }
|
237
|
+
it 'returns nil' do
|
238
|
+
expect(model.date_of_birth_year).to be 3334
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'makes the model invalid' do
|
242
|
+
expect(model).to be_invalid
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
describe 'as text' do
|
247
|
+
it 'accepts and converts them' do
|
248
|
+
model.date_of_birth_year = '1961'
|
249
|
+
expect(model.date_of_birth_year).to eq '1961'
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe 'when sent text' do
|
255
|
+
before { model.date_of_birth_year = 'Nineteen Sixty One' }
|
256
|
+
|
257
|
+
it 'sets the value' do
|
258
|
+
expect(model.date_of_birth_year).to eq 'Nineteen Sixty One'
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'makes the model invalid' do
|
262
|
+
expect(model).to be_invalid
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe 'when sent nil' do
|
267
|
+
it 'returns nil' do
|
268
|
+
model.date_of_birth_year = nil
|
269
|
+
expect(model.date_of_birth_year).to be_nil
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe 'when passed implausible dates' do
|
275
|
+
before do
|
276
|
+
model.date_of_birth_day = 30
|
277
|
+
model.date_of_birth_month = 2
|
278
|
+
model.date_of_birth_year = 2015
|
279
|
+
model.valid?
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'returns an error' do
|
283
|
+
err_msg = ["is not valid, '30-2-2015' is not a valid date"]
|
284
|
+
expect(model.errors[:date_of_birth]).to eq err_msg
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
describe 'set themselves but do not set date' do
|
289
|
+
describe '#posted_day' do
|
290
|
+
it 'can be set and returned' do
|
291
|
+
model.date_of_birth_day = 1
|
292
|
+
expect(model.date_of_birth_day).to eq 1
|
293
|
+
expect(model.date_of_birth).to be nil
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe '#posted_month' do
|
298
|
+
it 'can be set and returned' do
|
299
|
+
model.date_of_birth_month = 10
|
300
|
+
expect(model.date_of_birth_month).to eq 10
|
301
|
+
expect(model.date_of_birth).to be nil
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe '#posted_year' do
|
306
|
+
it 'can be set and returned' do
|
307
|
+
model.date_of_birth_year = 10
|
308
|
+
expect(model.date_of_birth_year).to eq 10
|
309
|
+
expect(model.date_of_birth).to be nil
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
describe 'set the date field by calling validate_date' do
|
315
|
+
context 'when all are completed' do
|
316
|
+
before(:each) { model.date_of_birth = nil }
|
317
|
+
|
318
|
+
it 'with year last' do
|
319
|
+
model.date_of_birth_day = 21
|
320
|
+
model.date_of_birth_month = 1
|
321
|
+
model.date_of_birth_year = 1961
|
322
|
+
expect(model.date_of_birth).to eql Date.new(1961, 1, 21)
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'with month last' do
|
326
|
+
model.date_of_birth_year = 1961
|
327
|
+
model.date_of_birth_day = 21
|
328
|
+
model.date_of_birth_month = 1
|
329
|
+
expect(model.date_of_birth).to eql Date.new(1961, 1, 21)
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'with day last' do
|
333
|
+
model.date_of_birth_month = 1
|
334
|
+
model.date_of_birth_year = 1961
|
335
|
+
model.date_of_birth_day = 21
|
336
|
+
expect(model.date_of_birth).to eql Date.new(1961, 1, 21)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|
343
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
RSpec::Matchers.define :have_attr_accessor do |field|
|
2
|
+
match do |object_instance|
|
3
|
+
object_instance.respond_to?(field) &&
|
4
|
+
object_instance.respond_to?("#{field}=")
|
5
|
+
end
|
6
|
+
|
7
|
+
failure_message do |object_instance|
|
8
|
+
"expected attr_accessor for #{field} on #{object_instance}"
|
9
|
+
end
|
10
|
+
|
11
|
+
failure_message_when_negated do |object_instance|
|
12
|
+
"expected attr_accessor for #{field} not to be defined on #{object_instance}"
|
13
|
+
end
|
14
|
+
|
15
|
+
description do
|
16
|
+
'assert there is an attr_accessor of the given name on the supplied object'
|
17
|
+
end
|
18
|
+
end
|
data/split_dmy.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'split_dmy/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'split_dmy'
|
8
|
+
spec.version = SplitDmy::VERSION
|
9
|
+
spec.authors = ['Colin Bruce']
|
10
|
+
spec.email = ['colinbruce@gmail.com']
|
11
|
+
spec.summary = 'Add split accessors for date fields /
|
12
|
+
into day, month, year parts'
|
13
|
+
spec.description = <<-EOF
|
14
|
+
Use `split_dmy_accessor :date_of_birth`to provide `date_of_birth_day`,
|
15
|
+
`date_of_birth_month`, `date_of_birth_year` accessors on the model.'
|
16
|
+
EOF
|
17
|
+
spec.homepage = 'https://github.com/CeeBeeUK/split_dmy'
|
18
|
+
spec.license = 'MIT'
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0")
|
21
|
+
spec.executables = spec.files.grep(%r{^/bin/$}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^/(test|spec|features)/$})
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.2'
|
28
|
+
spec.add_development_dependency 'activesupport', '~> 4.0.0'
|
29
|
+
spec.add_development_dependency 'activemodel', '~> 4.0.0'
|
30
|
+
spec.add_development_dependency 'activerecord', '~> 4.0.0'
|
31
|
+
spec.add_development_dependency 'sqlite3'
|
32
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
33
|
+
spec.add_development_dependency 'rubocop'
|
34
|
+
spec.add_development_dependency 'rubocop-rspec'
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: split_dmy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Colin Bruce
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.2'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.0.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 4.0.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activemodel
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 4.0.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 4.0.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activerecord
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 4.0.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 4.0.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sqlite3
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: codeclimate-test-reporter
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubocop-rspec
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: |2
|
154
|
+
Use `split_dmy_accessor :date_of_birth`to provide `date_of_birth_day`,
|
155
|
+
`date_of_birth_month`, `date_of_birth_year` accessors on the model.'
|
156
|
+
email:
|
157
|
+
- colinbruce@gmail.com
|
158
|
+
executables: []
|
159
|
+
extensions: []
|
160
|
+
extra_rdoc_files: []
|
161
|
+
files:
|
162
|
+
- ".gitignore"
|
163
|
+
- ".rubocop.yml"
|
164
|
+
- ".travis.yml"
|
165
|
+
- Gemfile
|
166
|
+
- LICENSE.txt
|
167
|
+
- README.md
|
168
|
+
- Rakefile
|
169
|
+
- lib/split_dmy.rb
|
170
|
+
- lib/split_dmy/date_validator.rb
|
171
|
+
- lib/split_dmy/split_accessors.rb
|
172
|
+
- lib/split_dmy/version.rb
|
173
|
+
- spec/spec_helper.rb
|
174
|
+
- spec/split_dmy/split_accessors_spec.rb
|
175
|
+
- spec/support/matchers/accessors_shared.rb
|
176
|
+
- spec/support/models.rb
|
177
|
+
- spec/support/schema.rb
|
178
|
+
- split_dmy.gemspec
|
179
|
+
homepage: https://github.com/CeeBeeUK/split_dmy
|
180
|
+
licenses:
|
181
|
+
- MIT
|
182
|
+
metadata: {}
|
183
|
+
post_install_message:
|
184
|
+
rdoc_options: []
|
185
|
+
require_paths:
|
186
|
+
- lib
|
187
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
188
|
+
requirements:
|
189
|
+
- - ">="
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
192
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
193
|
+
requirements:
|
194
|
+
- - ">="
|
195
|
+
- !ruby/object:Gem::Version
|
196
|
+
version: '0'
|
197
|
+
requirements: []
|
198
|
+
rubyforge_project:
|
199
|
+
rubygems_version: 2.4.4
|
200
|
+
signing_key:
|
201
|
+
specification_version: 4
|
202
|
+
summary: Add split accessors for date fields / into day, month, year parts
|
203
|
+
test_files: []
|