anodator 0.0.1

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