anodator 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +13 -0
  4. data/Gemfile.lock +28 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.ja.rdoc +33 -0
  7. data/README.rdoc +41 -0
  8. data/Rakefile +50 -0
  9. data/VERSION +1 -0
  10. data/example/example_01.rb +129 -0
  11. data/lib/anodator/anodator_error.rb +5 -0
  12. data/lib/anodator/check_result.rb +41 -0
  13. data/lib/anodator/checker.rb +58 -0
  14. data/lib/anodator/input_spec.rb +199 -0
  15. data/lib/anodator/input_spec_item.rb +33 -0
  16. data/lib/anodator/message.rb +59 -0
  17. data/lib/anodator/output_spec.rb +164 -0
  18. data/lib/anodator/rule.rb +97 -0
  19. data/lib/anodator/rule_set.rb +52 -0
  20. data/lib/anodator/utils.rb +234 -0
  21. data/lib/anodator/validator/base.rb +168 -0
  22. data/lib/anodator/validator/blank_validator.rb +14 -0
  23. data/lib/anodator/validator/complex_validator.rb +60 -0
  24. data/lib/anodator/validator/configuration_error.rb +8 -0
  25. data/lib/anodator/validator/date_validator.rb +151 -0
  26. data/lib/anodator/validator/format_validator.rb +48 -0
  27. data/lib/anodator/validator/inclusion_validator.rb +21 -0
  28. data/lib/anodator/validator/length_validator.rb +37 -0
  29. data/lib/anodator/validator/numeric_validator.rb +46 -0
  30. data/lib/anodator/validator/presence_validator.rb +14 -0
  31. data/lib/anodator/validator.rb +10 -0
  32. data/lib/anodator.rb +3 -0
  33. data/spec/anodator/check_result_spec.rb +101 -0
  34. data/spec/anodator/checker_spec.rb +273 -0
  35. data/spec/anodator/input_spec_item_spec.rb +100 -0
  36. data/spec/anodator/input_spec_spec.rb +584 -0
  37. data/spec/anodator/message_spec.rb +112 -0
  38. data/spec/anodator/output_spec_spec.rb +355 -0
  39. data/spec/anodator/rule_set_spec.rb +190 -0
  40. data/spec/anodator/rule_spec.rb +169 -0
  41. data/spec/anodator/validator/base_spec.rb +214 -0
  42. data/spec/anodator/validator/blank_validator_spec.rb +52 -0
  43. data/spec/anodator/validator/complex_validator_spec.rb +268 -0
  44. data/spec/anodator/validator/date_validator_spec.rb +350 -0
  45. data/spec/anodator/validator/format_validator_spec.rb +158 -0
  46. data/spec/anodator/validator/inclusion_validator_spec.rb +77 -0
  47. data/spec/anodator/validator/length_validator_spec.rb +236 -0
  48. data/spec/anodator/validator/numeric_validator_spec.rb +468 -0
  49. data/spec/anodator/validator/presence_validator_spec.rb +52 -0
  50. data/spec/anodator/validator_spec.rb +16 -0
  51. data/spec/anodator_spec.rb +2 -0
  52. data/spec/spec_helper.rb +12 -0
  53. metadata +188 -0
@@ -0,0 +1,59 @@
1
+ require "anodator/anodator_error"
2
+
3
+ module Anodator
4
+ class UnknownMessageAttributeError < AnodatorError ; end
5
+
6
+ # Message is error message builder.
7
+ #
8
+ # Message is generated with formated string for expand several keywords.
9
+ # Message#expand method is expand keywords by data_provider suppried by parameter.
10
+ class Message < String
11
+ # message
12
+ attr_reader :template
13
+
14
+ def initialize(template_message)
15
+ @template = template_message.to_s
16
+
17
+ if @template.split(//).size.zero?
18
+ raise ArgumentError.new("template_message cannot be blank")
19
+ end
20
+ end
21
+
22
+ # expand message with data_provider
23
+ #
24
+ # message keyword is expressed +[[target_expression::attribute]]+.
25
+ # data_provider is data source for current check target.
26
+ #
27
+ # - target_expression: specify target.
28
+ # - attribute: actual printed value
29
+ # - name: target name
30
+ # - number: target number
31
+ # - value: target actual data
32
+ def expand(data_provider)
33
+ @template.gsub(/\[\[([^:]+)::([^\]]+)\]\]/) do
34
+ spec_item = data_provider.spec_item_by_expression($1)
35
+ case $2
36
+ when "name"
37
+ spec_item.name
38
+ when "number"
39
+ spec_item.number
40
+ when "value"
41
+ data_provider[$1]
42
+ else
43
+ raise UnknownMessageAttributeError.new("Unknown message attribute '#{$2}'")
44
+ end
45
+ end
46
+ end
47
+
48
+ def validate_configuration
49
+ @template.gsub(/\[\[([^:]+)::([^\]]+)\]\]/) do
50
+ Validator::Base.values.spec_item_by_expression($1)
51
+ unless %W(name number value).include?($2)
52
+ raise UnknownMessageAttributeError.new("Unknown message attribute '#{$2}'")
53
+ end
54
+ end
55
+ rescue UnknownTargetExpressionError, UnknownMessageAttributeError => e
56
+ raise InvalidConfiguration.new(e.to_s)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,164 @@
1
+ module Anodator
2
+ class OutputSpec
3
+ TARGET_DATA = "DATA"
4
+ TARGET_ERROR = "ERROR"
5
+
6
+ VALID_SYMBOL_ITEMS = [
7
+ :target_numbers,
8
+ :target_names,
9
+ :target_values,
10
+ :error_message,
11
+ :error_level,
12
+ :error_count,
13
+ :warning_count,
14
+ :error_and_warning_count,
15
+ ]
16
+
17
+ attr_reader :items, :target, :include_no_error
18
+ attr_accessor :separator, :value_separator
19
+
20
+ def initialize(items = [], options = { })
21
+ @items = items.to_a
22
+ @target = TARGET_ERROR
23
+ @include_no_error = false
24
+ @separator = " "
25
+ @value_separator = ""
26
+
27
+ options.each do |key, opt|
28
+ case key
29
+ when :target
30
+ @target = opt
31
+ when :include_no_error
32
+ @include_no_error = !!opt
33
+ when :separator, :value_separator
34
+ @separator = opt.to_s
35
+ else
36
+ raise ArgumentError.new("unknown option #{key}.")
37
+ end
38
+ end
39
+
40
+ unless [TARGET_DATA, TARGET_ERROR].include?(@target)
41
+ raise ArgumentError.new("unknown target option value #{@target}.")
42
+ end
43
+
44
+ check_items
45
+ end
46
+
47
+ def validate_configuration
48
+ @items.each do |item|
49
+ if item.is_a? String
50
+ Validator::Base.values.spec_item_at_by_number(item)
51
+ end
52
+ end
53
+ rescue UnknownTargetExpressionError => e
54
+ raise InvalidConfiguration.new(e.to_s)
55
+ end
56
+
57
+ def check_items
58
+ @items.each do |item|
59
+ if item.is_a? Symbol
60
+ unless VALID_SYMBOL_ITEMS.include?(item)
61
+ raise ArgumentError.new("unknown item symbol #{item}")
62
+ end
63
+ end
64
+ end
65
+ end
66
+ private :check_items
67
+
68
+ def generate(input_spec_with_values, check_results)
69
+ if @target == TARGET_DATA
70
+ generate_data(input_spec_with_values, check_results)
71
+ else # @target == TARGET_ERROR
72
+ generate_error(input_spec_with_values, check_results)
73
+ end
74
+ end
75
+
76
+ def generate_data(input_spec_with_values, check_results)
77
+ buf = []
78
+ buf << @items.map do |item|
79
+ if item.is_a? Symbol
80
+ case item
81
+ when :error_count
82
+ next check_results.map { |result|
83
+ result.error? ? true : nil
84
+ }.compact.size.to_s
85
+ when :warning_count
86
+ next check_results.map { |result|
87
+ result.warning? ? true : nil
88
+ }.compact.size.to_s
89
+ when :error_and_warning_count
90
+ next check_results.size.to_s
91
+ else
92
+ next ""
93
+ end
94
+ else # data
95
+ next input_spec_with_values[item]
96
+ end
97
+ end
98
+
99
+ return buf
100
+ end
101
+ private :generate_data
102
+
103
+ def generate_error(input_spec_with_values, check_results)
104
+ buf = []
105
+
106
+ if check_results.size.zero?
107
+ if @include_no_error
108
+ buf << @items.map do |item|
109
+ if item.is_a? Symbol
110
+ case item
111
+ when :error_count, :warning_count, :error_and_warning_count
112
+ next "0"
113
+ else
114
+ next ""
115
+ end
116
+ else # data
117
+ next input_spec_with_values[item]
118
+ end
119
+ end
120
+ end
121
+ else
122
+ check_results.each do |check_result|
123
+ buf << @items.map do |item|
124
+ if item.is_a? Symbol
125
+ case item
126
+ when :target_numbers
127
+ next check_result.target_numbers.join(@separator)
128
+ when :target_names
129
+ next check_result.target_numbers.map { |number|
130
+ input_spec_with_values.spec_item_at_by_number(number).name
131
+ }.join(@separator)
132
+ when :target_values
133
+ next check_result.target_numbers.map { |number|
134
+ input_spec_with_values[number]
135
+ }.join(@value_separator)
136
+ when :error_message
137
+ next check_result.message
138
+ when :error_level
139
+ next check_result.level.to_s
140
+ when :error_count
141
+ next check_results.map { |result|
142
+ result.error? ? true : nil
143
+ }.compact.size.to_s
144
+ when :warning_count
145
+ next check_results.map { |result|
146
+ result.warning? ? true : nil
147
+ }.compact.size.to_s
148
+ when :error_and_warning_count
149
+ next check_results.size.to_s
150
+ else
151
+ next ""
152
+ end
153
+ else # data
154
+ next input_spec_with_values[item]
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ return buf
161
+ end
162
+ private :generate_error
163
+ end
164
+ end
@@ -0,0 +1,97 @@
1
+ require "anodator/message"
2
+ require "anodator/validator"
3
+ require "anodator/check_result"
4
+
5
+ module Anodator
6
+ # Check rule
7
+ #
8
+ # Rule has target expressions, prerequisite, validator and message.
9
+ # "Prerequisite" is represented by the Validator.
10
+ class Rule
11
+ # Check level ERROR
12
+ LEVEL_ERROR = 2
13
+ # Check level WARNING
14
+ LEVEL_WARNING = 1
15
+
16
+ attr_reader :target_expressions, :message, :validator, :prerequisite, :level, :description
17
+
18
+ def initialize(target_expressions, message, validator, prerequisite = nil, level = LEVEL_ERROR, description = nil)
19
+ @target_expressions = target_expressions.to_a
20
+ @message = message
21
+ @validator = validator
22
+ @prerequisite = prerequisite
23
+ @level = level
24
+ @description = description
25
+
26
+ if @target_expressions.size.zero?
27
+ raise ArgumentError.new("target expressions cannot be blank")
28
+ end
29
+ if @message.nil?
30
+ raise ArgumentError.new("message cannot be blank")
31
+ end
32
+ if @validator.nil?
33
+ raise ArgumentError.new("validator cannot be blank")
34
+ end
35
+ unless [LEVEL_ERROR, LEVEL_WARNING].include?(@level)
36
+ raise ArgumentError.new("level must be ERROR or WARNING")
37
+ end
38
+ end
39
+
40
+ # check values depend on prerequisite and validator
41
+ #
42
+ # When invalid, return CheckResult object, but when valid
43
+ # return nil.
44
+ def check
45
+ unless @prerequisite.nil?
46
+ unless @prerequisite.valid?
47
+ return nil
48
+ end
49
+ end
50
+
51
+ if @validator.valid?
52
+ return nil
53
+ else
54
+ numbers = @target_expressions.map do |target_expression|
55
+ Validator::Base.values.spec_item_by_expression(target_expression).number
56
+ end
57
+
58
+ CheckResult.new(numbers,
59
+ @message.expand(Validator::Base.values),
60
+ @level)
61
+ end
62
+ end
63
+
64
+ def validate_configuration
65
+ @target_expressions.each do |target_expression|
66
+ Validator::Base.values.spec_item_by_expression(target_expression)
67
+ end
68
+ @message.validate_configuration
69
+ @validator.validate_configuration
70
+ @prerequisite.validate_configuration unless @prerequisite.nil?
71
+ rescue UnknownTargetExpressionError => e
72
+ raise InvalidConfiguration.new(e.to_s)
73
+ end
74
+
75
+ def level_expression
76
+ if @level == LEVEL_ERROR
77
+ return "ERROR"
78
+ elsif @level == LEVEL_WARNING
79
+ return "WARNING"
80
+ end
81
+ end
82
+
83
+ def to_s
84
+ target_names = @target_expressions.map { |te| Validator::Base.values.spec_item_by_expression(te).name }.join(",")
85
+ buf =<<_EOD_
86
+ Description: #{@description.nil? ? "None." : @description}
87
+ Targets: #{target_names}
88
+ Message: #{@message.template}
89
+ Level: #{level_expression}
90
+ Validator:
91
+ #{@validator.to_s}
92
+ Prerequisite:
93
+ #{@prerequisite.nil? ? " - (None)" : @prerequisite.to_s}
94
+ _EOD_
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,52 @@
1
+ require "anodator/rule"
2
+
3
+ module Anodator
4
+ class RuleSet
5
+ def initialize
6
+ @rules = []
7
+ @results = []
8
+ end
9
+
10
+ def add_rule(rule)
11
+ if rule.is_a? Rule
12
+ @rules << rule
13
+ else
14
+ raise ArgumentError.new("rule must be Anodator::Rule object")
15
+ end
16
+ end
17
+
18
+ alias_method :<<, :add_rule
19
+
20
+ def check_all
21
+ @results = []
22
+
23
+ if @rules.count.zero?
24
+ return false
25
+ else
26
+ @rules.each do |rule|
27
+ if result = rule.check
28
+ @results << result
29
+ end
30
+ end
31
+
32
+ return true
33
+ end
34
+ end
35
+
36
+ def results
37
+ return @results
38
+ end
39
+
40
+ def validate_configuration
41
+ @rules.each do |rule|
42
+ rule.validate_configuration
43
+ end
44
+ end
45
+
46
+ def to_s
47
+ @rules.map { |rule|
48
+ rule.to_s
49
+ }.join("\n")
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,234 @@
1
+ require "csv"
2
+
3
+ module Anodator
4
+ module Utils
5
+ # load input_spec from csv file
6
+ #
7
+ # File encoding: UTF-8
8
+ # Columns:
9
+ # - column identification(string)
10
+ # - column name(string)
11
+ # - column type(STRING, NUMERIC or DATE, default is STRING)
12
+ # Arguments:
13
+ # - file_path: file path for input spec.
14
+ # Return:
15
+ # InputSpec instance
16
+ def self.load_input_spec_from_csv_file(file_path)
17
+ first = true
18
+ header = nil
19
+ spec = []
20
+ CSV.open(file_path, "r") do |row|
21
+ # skip header
22
+ if first
23
+ header = row
24
+ first = false
25
+ next
26
+ end
27
+ spec << { :number => row[0], :name => row[1], :type => row[2] }
28
+ end
29
+
30
+ return InputSpec.new(spec)
31
+ end
32
+
33
+ # load output_spec from csv file
34
+ #
35
+ # When target is TARGET_ERROR generate output line by one error,
36
+ # when target is TARGET_DATA generate output line by one data.
37
+ # When set include_no_error is true and target is TARGET_DATA,
38
+ # generate output line has no error.
39
+ #
40
+ # File encoding: UTF-8
41
+ # Columns:
42
+ # - output column identification(string)
43
+ # - output special column(string)
44
+ # - target_numbers(for error)
45
+ # - target_names(for one error)
46
+ # - target_values(for one error)
47
+ # - error_message(for one error)
48
+ # - error_level(for one error)
49
+ # - error_count(for one error)
50
+ # - warning_count(for one data)
51
+ # - error_and_warning_count(for one data)
52
+ # Arguments:
53
+ # - file_path: file path for output spec.
54
+ # - target: OutputSpec::TARGET_ERROR or OutputSpec::TARGET_DATA
55
+ # - include_no_error: true or false(default false)
56
+ # Return:
57
+ # OutputSpec instance
58
+ def self.load_output_spec_from_csv_file(file_path,
59
+ target = Anodator::OutputSpec::TARGET_ERROR,
60
+ include_no_error = false)
61
+ first = true
62
+ header = nil
63
+ spec = []
64
+ CSV.open(file_path, "r") do |row|
65
+ # skip header
66
+ if first
67
+ header = row
68
+ first = false
69
+ next
70
+ end
71
+ if row.first.nil? || row.first.split(//).length.zero?
72
+ spec << row.last.to_sym
73
+ else
74
+ spec << row.first.to_s
75
+ end
76
+ end
77
+
78
+ return Anodator::OutputSpec.new(spec, :target => target, :include_no_error => include_no_error)
79
+ end
80
+
81
+ # options reader for option values for validator specs
82
+ #
83
+ # When value is 'true' or 'false', convert true or false instance.
84
+ # The others set to string.
85
+ #
86
+ # Arguments:
87
+ # - row: validator line string
88
+ # - options: default options
89
+ # - options_index_from: index for option column be started on row(default: 4)
90
+ # Return:
91
+ # Hash
92
+ def self.options_reader(row, options = { }, options_index_from = 4)
93
+ if row.size > options_index_from
94
+ row[options_index_from..-1].each do |column|
95
+ next if column.nil?
96
+ key, value = column.split(":", 2)
97
+ case value
98
+ when "true", "false"
99
+ options[key.to_sym] = eval(value)
100
+ else
101
+ options[key.to_sym] = value
102
+ end
103
+ end
104
+ end
105
+
106
+ return options
107
+ end
108
+
109
+ # load validators from csv file
110
+ #
111
+ # File encoding: UTF-8
112
+ # Columns:
113
+ # - validator identification(string)
114
+ # - validation name(string)
115
+ # - validator type(string)
116
+ # - target expression(string)
117
+ # - options...
118
+ # Return:
119
+ # Hash for validators(key is identification, value is validator)
120
+ def self.load_validators_from_csv_file(file_path)
121
+ first = true
122
+ header = nil
123
+ validators = { }
124
+
125
+ CSV.open(file_path, "r") do |row|
126
+ # skip header
127
+ if first
128
+ header = row
129
+ first = false
130
+ next
131
+ end
132
+
133
+ if validators.keys.include?(row[0])
134
+ raise ArgumentError.new("Duplicated validator number '#{row[0]}'!")
135
+ end
136
+
137
+ options = options_reader(row, { :description => row[1] })
138
+
139
+ validator = nil
140
+
141
+ case row[2]
142
+ when "presence"
143
+ validator = Validator::PresenceValidator
144
+ when "blank"
145
+ validator = Validator::BlankValidator
146
+ when "date"
147
+ validator = Validator::DateValidator
148
+ when "format"
149
+ validator = Validator::FormatValidator
150
+ when "inclusion"
151
+ validator = Validator::InclusionValidator
152
+ unless options[:in].nil?
153
+ options[:in] = options[:in].split(",")
154
+ end
155
+ when "length"
156
+ validator = Validator::LengthValidator
157
+ when "numeric"
158
+ validator = Validator::NumericValidator
159
+ when "complex"
160
+ options[:validators] = row[3].split(",").map do |validator_number|
161
+ validators[validator_number]
162
+ end
163
+ validators[row[0]] = Validator::ComplexValidator.new(options)
164
+ else
165
+ raise ArgumentError.new("Unknown validator type '#{row[2]}'!")
166
+ end
167
+
168
+ if validator
169
+ validators[row[0]] = validator.new(row[3], options)
170
+ end
171
+ end
172
+
173
+ return validators
174
+ end
175
+
176
+ # load rule_set from csv file
177
+ #
178
+ # File encoding: UTF-8
179
+ # Columns:
180
+ # - rule identification(string)
181
+ # - rule description(string)
182
+ # - target expression(column identification or column name)
183
+ # - validator identification
184
+ # - prerequisite validator identification(allow blank)
185
+ # - error level(ERROR or WARNING)
186
+ # - error message holder(string)
187
+ # Return:
188
+ # RuleSet instance
189
+ def self.load_rule_from_csv_file(file_path, validators)
190
+ first = true
191
+ header = nil
192
+ rule_set = RuleSet.new
193
+
194
+ CSV.open(file_path, "r") do |row|
195
+ # skip header
196
+ if first
197
+ header = row
198
+ first = false
199
+ next
200
+ end
201
+
202
+ description = row[1]
203
+ target_expression = row[2].split(",")
204
+ validator = validators[row[3]]
205
+ prerequisite = validators[row[4]]
206
+ if validator.nil?
207
+ raise "Unknown validator identifier '#{row[3]}'"
208
+ end
209
+ if !row[4].nil? && prerequisite.nil?
210
+ raise "Unknown validator identifier '#{row[4]}'"
211
+ end
212
+ level = case row[5]
213
+ when "ERROR"
214
+ Rule::LEVEL_ERROR
215
+ when "WARNING"
216
+ Rule::LEVEL_WARNING
217
+ else
218
+ raise "Unknown error type '#{row[5]}'"
219
+ end
220
+ message = Message.new(row[6])
221
+
222
+ rule_set <<
223
+ Rule.new(target_expression,
224
+ message,
225
+ validator,
226
+ prerequisite,
227
+ level,
228
+ description)
229
+ end
230
+
231
+ return rule_set
232
+ end
233
+ end
234
+ end