dsu 0.1.0.alpha.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,11 +6,10 @@ require_relative '../support/configuration'
6
6
 
7
7
  module Dsu
8
8
  module Services
9
+ # This class loads an entry group file.
9
10
  class ConfigurationLoaderService
10
11
  include Dsu::Support::Configuration
11
12
 
12
- attr_reader :default_options
13
-
14
13
  def initialize(default_options: nil)
15
14
  unless default_options.nil? ||
16
15
  default_options.is_a?(Hash) ||
@@ -18,7 +17,7 @@ module Dsu
18
17
  raise ArgumentError, 'default_options must be a Hash or ActiveSupport::HashWithIndifferentAccess'
19
18
  end
20
19
 
21
- @default_options ||= default_options || {}
20
+ @default_options = default_options || {}
22
21
  @default_options = @default_options.with_indifferent_access if @default_options.is_a?(Hash)
23
22
  end
24
23
 
@@ -28,6 +27,8 @@ module Dsu
28
27
 
29
28
  private
30
29
 
30
+ attr_reader :default_options
31
+
31
32
  def config_options
32
33
  return Support::Configuration::DEFAULT_DSU_OPTIONS unless config_file?
33
34
 
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../models/entry'
4
+ require_relative '../support/colorable'
5
+ require_relative '../support/say'
6
+ require_relative '../support/time_formatable'
7
+ require_relative '../views/edited_entries/shared/errors'
8
+ require_relative '../views/shared/messages'
9
+ require_relative 'configuration_loader_service'
10
+ require_relative 'stdout_redirector_service'
11
+
12
+ module Dsu
13
+ module Services
14
+ class EntryGroupEditorService
15
+ include Support::Colorable
16
+ include Support::Say
17
+ include Support::TimeFormatable
18
+
19
+ def initialize(entry_group:, options: {})
20
+ raise ArgumentError, 'entry_group is nil' if entry_group.nil?
21
+ raise ArgumentError, 'entry_group is the wrong object type' unless entry_group.is_a?(Models::EntryGroup)
22
+ raise ArgumentError, 'options is the wrong object type' unless options.is_a?(Hash) || options.nil?
23
+
24
+ @entry_group = entry_group
25
+ @options = options || {}
26
+ end
27
+
28
+ def call
29
+ edit_view = render_edit_view
30
+ edit edit_view
31
+ # NOTE: Return the original entry group object as any permanent changes
32
+ # will have been applied to it.
33
+ entry_group
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :entry_group, :options
39
+
40
+ # Renders the edit view to a string so we can write it to a temporary file
41
+ # and edit it. The edits will be used to update the entry group.
42
+ def render_edit_view
43
+ say "Editing entry group #{formatted_time(time: entry_group.time)}...", HIGHLIGHT
44
+ StdoutRedirectorService.call { Views::EntryGroup::Edit.new(entry_group: entry_group).render }
45
+ end
46
+
47
+ # Writes the temporary file contents to disk and opens it in the editor
48
+ # for editing. It then copies the changes to the entry group and writes
49
+ # the changes to the entry group file.
50
+ def edit(edit_view)
51
+ entry_group_with_edits = Models::EntryGroup.new(time: entry_group.time)
52
+
53
+ Services::TempFileWriterService.new(tmp_file_content: edit_view).call do |tmp_file_path|
54
+ if Kernel.system("${EDITOR:-#{configuration[:editor]}} #{tmp_file_path}")
55
+ Services::TempFileReaderService.new(tmp_file_path: tmp_file_path).call do |editor_line|
56
+ next unless process_description?(editor_line)
57
+
58
+ entry_group_with_edits.entries << Models::Entry.new(description: editor_line)
59
+ end
60
+
61
+ process_entry_group!(entry_group_with_edits)
62
+ else
63
+ say "Failed to open temporary file in editor '#{configuration[:editor]}'; " \
64
+ "the system error returned was: '#{$CHILD_STATUS}'.", ERROR
65
+ say 'Either set the EDITOR environment variable ' \
66
+ 'or set the dsu editor configuration option (`$ dsu config init`).', ERROR
67
+ say 'Run `$ dsu help config` for more information:', ERROR
68
+ end
69
+ end
70
+ end
71
+
72
+ def process_entry_group!(entry_group_with_edits)
73
+ if entry_group_with_edits.entries.empty?
74
+ entry_group.delete!
75
+ return
76
+ end
77
+
78
+ if entry_group_with_edits.invalid?
79
+ header = 'The following ERRORS were encountered; these changes were not saved:'
80
+ messages = entry_group_with_edits.errors.full_messages
81
+ Views::Shared::Messages.new(messages: messages, message_type: :error, options: { header: header }).render
82
+ end
83
+
84
+ # Make sure we're saving only valid, unique entries.
85
+ entry_group.entries = entry_group_with_edits.valid_unique_entries
86
+ entry_group.save!
87
+ end
88
+
89
+ def process_description?(description)
90
+ description = Models::Entry.clean_description(description)
91
+ !(description.blank? || description[0] == '#')
92
+ end
93
+
94
+ def configuration
95
+ @configuration ||= ConfigurationLoaderService.new.call
96
+ end
97
+ end
98
+ end
99
+ end
@@ -26,6 +26,7 @@ module Dsu
26
26
  entry_group.validate!
27
27
  create_entry_group_path_if!
28
28
  write_entry_group_to_file!
29
+ entry_group
29
30
  rescue ActiveModel::ValidationError
30
31
  puts "Error(s) encountered: #{entry_group.errors.full_messages}"
31
32
  raise
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dsu
4
+ module Services
5
+ # This service captures $stdout, resirects it to a StringIO object,
6
+ # and returns the string value.
7
+ # https://stackoverflow.com/questions/4459330/how-do-i-temporarily-redirect-stderr-in-ruby/4459463#4459463
8
+ module StdoutRedirectorService
9
+ class << self
10
+ def call
11
+ raise ArgumentError, 'no block was provided' unless block_given?
12
+
13
+ # The output stream must be an IO-like object. In this case we capture it in
14
+ # an in-memory IO object so we can return the string value. Any IO object can
15
+ # be used here.
16
+ string_io = StringIO.new
17
+ original_stdout, $stdout = $stdout, string_io # rubocop:disable Style/ParallelAssignment
18
+ yield
19
+ string_io.string
20
+ ensure
21
+ # Restore the original $stdout.
22
+ $stdout = original_stdout
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -3,29 +3,29 @@
3
3
  module Dsu
4
4
  module Services
5
5
  class TempFileReaderService
6
- def initialize(temp_file_path:, options: {})
7
- raise ArgumentError, 'temp_file_path is nil' if temp_file_path.nil?
8
- raise ArgumentError, 'temp_file_path is the wrong object type' unless temp_file_path.is_a?(String)
9
- raise ArgumentError, 'temp_file_path is empty' if temp_file_path.empty?
10
- raise ArgumentError, 'temp_file_path does not exist' unless File.exist?(temp_file_path)
6
+ def initialize(tmp_file_path:, options: {})
7
+ raise ArgumentError, 'tmp_file_path is nil' if tmp_file_path.nil?
8
+ raise ArgumentError, 'tmp_file_path is the wrong object type' unless tmp_file_path.is_a?(String)
9
+ raise ArgumentError, 'tmp_file_path is empty' if tmp_file_path.empty?
10
+ raise ArgumentError, 'tmp_file_path does not exist' unless File.exist?(tmp_file_path)
11
11
  raise ArgumentError, 'options is nil' if options.nil?
12
12
  raise ArgumentError, 'options is the wrong object type' unless options.is_a?(Hash)
13
13
 
14
- @temp_file_path = temp_file_path
14
+ @tmp_file_path = tmp_file_path
15
15
  @options = options || {}
16
16
  end
17
17
 
18
18
  def call
19
19
  raise ArgumentError, 'no block given' unless block_given?
20
20
 
21
- File.foreach(temp_file_path) do |line|
21
+ File.foreach(tmp_file_path) do |line|
22
22
  yield line.strip
23
23
  end
24
24
  end
25
25
 
26
26
  private
27
27
 
28
- attr_reader :temp_file_path, :options
28
+ attr_reader :tmp_file_path, :options
29
29
  end
30
30
  end
31
31
  end
@@ -5,13 +5,13 @@ require 'tempfile'
5
5
  module Dsu
6
6
  module Services
7
7
  class TempFileWriterService
8
- def initialize(temp_file_content:, options: {})
9
- raise ArgumentError, 'temp_file_content is nil' if temp_file_content.nil?
10
- raise ArgumentError, 'temp_file_content is the wrong object type' unless temp_file_content.is_a?(String)
8
+ def initialize(tmp_file_content:, options: {})
9
+ raise ArgumentError, 'tmp_file_content is nil' if tmp_file_content.nil?
10
+ raise ArgumentError, 'tmp_file_content is the wrong object type' unless tmp_file_content.is_a?(String)
11
11
  raise ArgumentError, 'options is nil' if options.nil?
12
12
  raise ArgumentError, 'options is the wrong object type' unless options.is_a?(Hash)
13
13
 
14
- @temp_file_content = temp_file_content
14
+ @tmp_file_content = tmp_file_content
15
15
  @options = options || {}
16
16
  end
17
17
 
@@ -19,7 +19,7 @@ module Dsu
19
19
  raise ArgumentError, 'no block given' unless block_given?
20
20
 
21
21
  Tempfile.new('dsu').tap do |file|
22
- file.write("#{temp_file_content}\n")
22
+ file.write("#{tmp_file_content}\n")
23
23
  file.close
24
24
  yield file.path
25
25
  end.unlink
@@ -27,7 +27,7 @@ module Dsu
27
27
 
28
28
  private
29
29
 
30
- attr_reader :temp_file_content, :options
30
+ attr_reader :tmp_file_content, :options
31
31
  end
32
32
  end
33
33
  end
@@ -1,19 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'English'
4
3
  require_relative '../base_cli'
5
4
  require_relative '../models/entry_group'
6
- require_relative '../services/temp_file_reader_service'
7
- require_relative '../services/temp_file_writer_service'
8
- require_relative '../support/time_formatable'
9
- require_relative '../views/entry_group/edit'
10
5
  require_relative '../views/entry_group/show'
11
6
 
12
7
  module Dsu
13
8
  module Subcommands
14
9
  class Edit < Dsu::BaseCLI
15
- include Support::TimeFormatable
16
-
17
10
  map %w[d] => :date
18
11
  map %w[n] => :today
19
12
  map %w[t] => :tomorrow
@@ -25,7 +18,8 @@ module Dsu
25
18
  Edits the DSU entries for today.
26
19
  LONG_DESC
27
20
  def today
28
- Views::EntryGroup::Show.new(entry_group: edit_entry_group(time: Time.now)).render
21
+ entry_group = Models::EntryGroup.edit(time: Time.now)
22
+ Views::EntryGroup::Show.new(entry_group: entry_group).render
29
23
  end
30
24
 
31
25
  desc 'tomorrow, t',
@@ -34,7 +28,8 @@ module Dsu
34
28
  Edits the DSU entries for tomorrow.
35
29
  LONG_DESC
36
30
  def tomorrow
37
- Views::EntryGroup::Show.new(entry_group: edit_entry_group(time: Time.now.tomorrow)).render
31
+ entry_group = Models::EntryGroup.edit(time: Time.now.tomorrow)
32
+ Views::EntryGroup::Show.new(entry_group: entry_group).render
38
33
  end
39
34
 
40
35
  desc 'yesterday, y',
@@ -43,7 +38,8 @@ module Dsu
43
38
  Edits the DSU entries for yesterday.
44
39
  LONG_DESC
45
40
  def yesterday
46
- Views::EntryGroup::Show.new(entry_group: edit_entry_group(time: Time.now.yesterday)).render
41
+ entry_group = Models::EntryGroup.edit(time: Time.now.yesterday)
42
+ Views::EntryGroup::Show.new(entry_group: entry_group).render
47
43
  end
48
44
 
49
45
  desc 'date, d DATE',
@@ -54,79 +50,12 @@ module Dsu
54
50
  \x5 #{date_option_description}
55
51
  LONG_DESC
56
52
  def date(date)
57
- Views::EntryGroup::Show.new(entry_group: edit_entry_group(time: Time.parse(date))).render
53
+ entry_group = Models::EntryGroup.edit(time: Time.parse(date))
54
+ Views::EntryGroup::Show.new(entry_group: entry_group).render
58
55
  rescue ArgumentError => e
59
56
  say "Error: #{e.message}", ERROR
60
57
  exit 1
61
58
  end
62
-
63
- private
64
-
65
- def edit_entry_group(time:)
66
- formatted_time = formatted_time(time: time)
67
- unless Models::EntryGroup.exists?(time: time)
68
- say "No DSU entries exist for #{formatted_time}"
69
- exit 1
70
- end
71
-
72
- say "Editing DSU entries for #{formatted_time}..."
73
- entry_group = Models::EntryGroup.load(time: time)
74
-
75
- # This renders the view to a string...
76
- output = capture_stdxxx do
77
- Views::EntryGroup::Edit.new(entry_group: entry_group).render
78
- end
79
- # ...which is then written to a temp file.
80
- Services::TempFileWriterService.new(temp_file_content: output).call do |temp_file_path|
81
- unless system("${EDITOR:-#{configuration[:editor]}} #{temp_file_path}")
82
- say "Failed to open temporary file in editor '#{configuration[:editor]}';" \
83
- "the system error returned was: '#{$CHILD_STATUS}'.", ERROR
84
- say 'Either set the EDITOR environment variable ' \
85
- 'or set the dsu editor configuration option (`$ dsu config init`).', ERROR
86
- say 'Run `$ dsu help config` for more information.', ERROR
87
- system('dsu help config')
88
- exit 1
89
- end
90
- entries = []
91
- Services::TempFileReaderService.new(temp_file_path: temp_file_path).call do |temp_file_line|
92
- # Skip comments and blank lines.
93
- next if ['#', nil].include? temp_file_line[0]
94
-
95
- match_data = temp_file_line.match(/(\S+)\s(.+)/)
96
- # TODO: Error handling if match_data is nil.
97
- entry_sha = match_data[1]
98
- entry_description = match_data[2]
99
-
100
- next if %w[- d delete].include?(entry_sha) # delete the entry
101
-
102
- entry_sha = nil if %w[+ a add].include?(entry_sha) # add the new entry
103
- entries << Models::Entry.new(uuid: entry_sha, description: entry_description)
104
- end
105
-
106
- entry_group.entries = entries
107
-
108
- return entry_group.delete if entries.empty?
109
-
110
- entry_group.save!
111
- end
112
- entry_group
113
- end
114
-
115
- # https://stackoverflow.com/questions/4459330/how-do-i-temporarily-redirect-stderr-in-ruby/4459463#4459463
116
- def capture_stdxxx
117
- # The output stream must be an IO-like object. In this case we capture it in
118
- # an in-memory IO object so we can return the string value. You can assign any
119
- # IO object here.
120
- string_io = StringIO.new
121
- prev_stdout, $stdout = $stdout, string_io # rubocop:disable Style/ParallelAssignment
122
- prev_stderr, $stderr = $stderr, string_io # rubocop:disable Style/ParallelAssignment
123
- yield
124
- string_io.string
125
- ensure
126
- # Restore the previous value of stderr and stdout (typically equal to STDERR).
127
- $stdout = prev_stdout
128
- $stderr = prev_stderr
129
- end
130
59
  end
131
60
  end
132
61
  end
@@ -6,6 +6,7 @@ module Dsu
6
6
  ABORTED = :red
7
7
  ERROR = :red
8
8
  HIGHLIGHT = :cyan
9
+ INFO = HIGHLIGHT
9
10
  SUCCESS = :green
10
11
  WARNING = :yellow
11
12
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dsu
4
+ module Support
5
+ module Descriptable
6
+ class << self
7
+ def included(mod)
8
+ mod.extend(ClassMethods)
9
+ end
10
+ end
11
+
12
+ def short_description
13
+ return '' if description.blank?
14
+
15
+ self.class.short_description(string: description)
16
+ end
17
+
18
+ module ClassMethods
19
+ def short_description(string:, count: 25, elipsis: '...')
20
+ return elipsis unless string.is_a?(String)
21
+
22
+ elipsis_length = elipsis.length
23
+ count = elipsis_length if count.nil? || count < elipsis_length
24
+
25
+ return string if string.length <= count
26
+
27
+ tokens = string.split
28
+ string = ''
29
+
30
+ return "#{tokens.first[0..(count - elipsis_length)]}#{elipsis}" if tokens.count == 1
31
+
32
+ tokens.each do |token|
33
+ break if string.length + token.length + elipsis_length > count
34
+
35
+ string = "#{string} #{token}"
36
+ end
37
+
38
+ "#{string.strip}#{elipsis}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -3,6 +3,7 @@
3
3
  require 'pathname'
4
4
  require_relative '../services/entry_group_reader_service'
5
5
  require_relative '../models/entry'
6
+ require_relative '../models/entry_group'
6
7
 
7
8
  module Dsu
8
9
  module Support
@@ -12,22 +13,19 @@ module Dsu
12
13
  # returns a Hash having :time and :entries
13
14
  # where entries == an Array of Entry Hashes
14
15
  # representing the JSON Entry objects for :time.
15
- def entry_group_hash_for(time:)
16
+ def load_entry_group_file_for(time:)
16
17
  entry_group_json = Services::EntryGroupReaderService.new(time: time).call
17
- if entry_group_json.present?
18
- return JSON.parse(entry_group_json, symbolize_names: true).tap do |hash|
18
+ hash = if entry_group_json.present?
19
+ JSON.parse(entry_group_json, symbolize_names: true).tap do |hash|
19
20
  hash[:time] = Time.parse(hash[:time])
20
21
  end
22
+ else
23
+ { time: time, entries: [] }
21
24
  end
22
25
 
23
- {
24
- time: time,
25
- entries: []
26
- }
26
+ Models::EntryGroup.new(**hydrate_entry_group_hash(hash: hash, time: time))
27
27
  end
28
28
 
29
- private
30
-
31
29
  # Accepts an entry group hash and returns a
32
30
  # hydrated entry group hash:
33
31
  #
@@ -39,10 +37,10 @@ module Dsu
39
37
  # ...
40
38
  # ]
41
39
  # }
42
- def hydrate_entry_group_hash(entry_group_hash:, time:)
43
- time = entry_group_hash.fetch(:time, time)
40
+ def hydrate_entry_group_hash(hash:, time:)
41
+ time = hash.fetch(:time, time)
44
42
  time = Time.parse(time) unless time.is_a? Time
45
- entries = entry_group_hash.fetch(:entries, [])
43
+ entries = hash.fetch(:entries, [])
46
44
  entries = entries.map { |entry_hash| Models::Entry.new(**entry_hash) }
47
45
 
48
46
  { time: time, entries: entries }
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dsu
4
+ module Validators
5
+ class DescriptionValidator < ActiveModel::Validator
6
+ def validate(record)
7
+ description = record.description
8
+
9
+ if description.blank?
10
+ record.errors.add(:description, :blank)
11
+ return
12
+ end
13
+
14
+ unless description.is_a?(String)
15
+ record.errors.add(field, 'is the wrong object type. ' \
16
+ "\"String\" was expected, but \"#{description.class}\" was received.")
17
+ return
18
+ end
19
+
20
+ validate_description record
21
+ end
22
+
23
+ private
24
+
25
+ def validate_description(record)
26
+ description = record.description
27
+
28
+ return if description.length.between?(2, 256)
29
+
30
+ if description.length < 2
31
+ record.errors.add(:description, "is too short: \"#{record.short_description}\" (minimum is 2 characters).")
32
+ elsif description.length > 256
33
+ record.errors.add(:description, "is too long: \"#{record.short_description}\" (maximum is 256 characters).")
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -7,55 +7,67 @@ require_relative '../support/field_errors'
7
7
  module Dsu
8
8
  module Validators
9
9
  class EntriesValidator < ActiveModel::Validator
10
- include Dsu::Support::FieldErrors
10
+ include Support::FieldErrors
11
11
 
12
12
  def validate(record)
13
- raise 'options[:fields] is not defined.' unless options.key? :fields
14
- raise 'options[:fields] is not an Array.' unless options[:fields].is_a? Array
15
- raise 'options[:fields] elements are not Symbols.' unless options[:fields].all?(Symbol)
13
+ unless record.entries.is_a?(Array)
14
+ record.errors.add(:entries_entry, 'is the wrong object type. ' \
15
+ "\"Array\" was expected, but \"#{record.entries.class}\" was received.")
16
+ end
16
17
 
17
- options[:fields].each do |field|
18
- entries = record.send(field)
18
+ validate_entry_types record
19
+ validate_unique_entry record
20
+ validate_entries record
21
+ end
19
22
 
20
- unless entries.is_a?(Array)
21
- record.errors.add(field, 'is the wrong object type. ' \
22
- "\"Array\" was expected, but \"#{entries.class}\" was received.")
23
- next
24
- end
23
+ private
25
24
 
26
- validate_entry_types field, entries, record
27
- validate_unique_entry_uuids field, entries, record
25
+ def validate_entry_types(record)
26
+ record.entries.each do |entry|
27
+ next if entry.is_a? Dsu::Models::Entry
28
+
29
+ record.errors.add(:entries_entry, 'entry Array element is the wrong object type. ' \
30
+ "\"Entry\" was expected, but \"#{entry.class}\" was received.",
31
+ type: Support::FieldErrors::FIELD_TYPE_ERROR)
28
32
  end
29
33
  end
30
34
 
31
- private
35
+ def validate_unique_entry(record)
36
+ return unless record.entries.is_a? Array
32
37
 
33
- def validate_entry_types(field, entries, record)
34
- entries.each do |entry|
35
- next if entry.is_a? Dsu::Models::Entry
38
+ entry_objects = record.entries.select { |entry| entry.is_a?(Dsu::Models::Entry) }
36
39
 
37
- record.errors.add(field, 'entry Array element is the wrong object type. ' \
38
- "\"Entry\" was expected, but \"#{entry.class}\" was received.",
39
- type: Dsu::Support::FieldErrors::FIELD_TYPE_ERROR)
40
+ descriptions = entry_objects.map(&:description)
41
+ return if descriptions.uniq.length == descriptions.length
40
42
 
41
- next
43
+ non_unique_descriptions = descriptions.select { |description| descriptions.count(description) > 1 }.uniq
44
+ if non_unique_descriptions.any?
45
+ record.errors.add(:entries_entry, 'contains a duplicate entry: ' \
46
+ "#{format_non_unique_descriptions(non_unique_descriptions)}.",
47
+ type: Support::FieldErrors::FIELD_DUPLICATE_ERROR)
42
48
  end
43
49
  end
44
50
 
45
- def validate_unique_entry_uuids(field, entries, record)
46
- return unless entries.is_a? Array
47
-
48
- entry_objects = entries.select { |entry| entry.is_a?(Dsu::Models::Entry) }
51
+ def validate_entries(record)
52
+ entries = record.entries
53
+ return if entries.none?
49
54
 
50
- uuids = entry_objects.map(&:uuid)
51
- return if uuids.uniq.length == uuids.length
55
+ entries.each do |entry|
56
+ next if entry.valid?
52
57
 
53
- non_unique_uuids = uuids.select { |element| uuids.count(element) > 1 }.uniq
54
- if non_unique_uuids.any?
55
- record.errors.add(field, "contains duplicate UUIDs: #{non_unique_uuids.join(', ')}.",
56
- type: Dsu::Support::FieldErrors::FIELD_DUPLICATE_ERROR)
58
+ entry.errors.each do |error|
59
+ record.errors.add(:entries_entry, error.full_message)
60
+ end
57
61
  end
58
62
  end
63
+
64
+ def format_non_unique_descriptions(non_unique_descriptions)
65
+ non_unique_descriptions.map { |description| "\"#{short_description(description)}\"" }.join(', ')
66
+ end
67
+
68
+ def short_description(description)
69
+ Models::Entry.short_description(string: description)
70
+ end
59
71
  end
60
72
  end
61
73
  end
@@ -5,29 +5,20 @@ module Dsu
5
5
  module Validators
6
6
  class TimeValidator < ActiveModel::Validator
7
7
  def validate(record)
8
- raise 'options[:fields] is not defined.' unless options.key? :fields
9
- raise 'options[:fields] is not an Array.' unless options[:fields].is_a? Array
10
- raise 'options[:fields] elements are not Symbols.' unless options[:fields].all?(Symbol)
8
+ time = record.time
11
9
 
12
- options[:fields].each do |field|
13
- time = record.send(field)
14
-
15
- if time.nil?
16
- record.errors.add(field, :blank)
17
- next
18
- end
19
-
20
- unless time.is_a?(Time)
21
- record.errors.add(field, 'is the wrong object type. ' \
22
- "\"Time\" was expected, but \"#{time.class}\" was received.")
23
- next
24
- end
10
+ if time.nil?
11
+ record.errors.add(:time, :blank)
12
+ return
13
+ end
25
14
 
26
- if time.utc?
27
- record.errors.add(field, 'is not in localtime format.')
28
- next
29
- end
15
+ unless time.is_a?(Time)
16
+ record.errors.add(:time, 'is the wrong object type. ' \
17
+ "\"Time\" was expected, but \"#{time.class}\" was received.")
18
+ return
30
19
  end
20
+
21
+ record.errors.add(:time, 'is not in localtime format.') if time.utc?
31
22
  end
32
23
  end
33
24
  end
data/lib/dsu/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dsu
4
- VERSION = '0.1.0.alpha.4'
4
+ VERSION = '1.0.0'
5
5
  end