module JCR
Copyright © 2017 American Registry for Internet Numbers
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Copyright © 2017 American Registry for Internet Numbers
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Copyright © 2016-2017 American Registry for Internet Numbers
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Constants
- VERSION
Public Class Methods
# File lib/jcr/evaluate_rules.rb, line 524 def self.annotations_to_s( annotations ) retval = "" annotations.each do |a| case when a[:unordered_annotation] retval = retval + "@{unordered}" when a[:not_annotation] retval = retval + "@{not}" when a[:root_annotation] retval = retval + "@{root}" else retval = retval + "@{ ** unknown annotation ** }" end end if annotations retval = retval + " " if retval.length != 0 return retval end
# File lib/jcr/evaluate_array_rules.rb, line 305 def self.array_to_s( jcr, shallow=true ) rules, annotations = get_rules_and_annotations( jcr ) return "#{annotations_to_s( annotations)}[ #{rules_to_s( rules, shallow )} ]" end
# File lib/jcr/evaluate_value_rules.rb, line 404 def self.bad_value jcr, rule_atom, expected, actual Evaluation.new( false, "expected << #{expected} >> but got << #{actual} >> for #{raised_rule(jcr,rule_atom)}" ) end
# File lib/jcr/jcr.rb, line 177 def self.breakup_message( message, line_length ) line = message.gsub(/(.{1,#{line_length}})(\s+|\Z)/, "\\1\n") lines = [] line.each_line do |l| lines << l.strip end return lines end
# File lib/jcr/check_groups.rb, line 107 def self.check_array_for_group node, mapping if node.is_a?( Array ) node.each do |child_node| check_array_for_group( child_node, mapping ) end elsif node.is_a? Hash if node[:target_rule_name] trule = get_name_mapping(node[:target_rule_name][:rule_name], mapping) disallowed_group_in_array?(trule, mapping) elsif node[:group_rule] disallowed_group_in_array?(node[:group_rule], mapping) end end end
# File lib/jcr/check_groups.rb, line 21 def self.check_groups( tree, mapping ) if tree.is_a? Array tree.each do |node| check_groups( node, mapping ) end else # is a hash if tree[:rule] check_groups( tree[:rule], mapping ) elsif tree[:primitive_rule] check_value_for_group( tree[:primitive_rule], mapping ) elsif tree[:member_rule] check_member_for_group( tree[:member_rule], mapping ) elsif tree[:array_rule] check_array_for_group( tree[:array_rule], mapping ) elsif tree[:object_rule] check_object_for_group( tree[:object_rule], mapping ) end end end
# File lib/jcr/check_groups.rb, line 72 def self.check_member_for_group node, mapping if node.is_a? Array node = node[0] end if node[:target_rule_name] trule = get_name_mapping( node[:target_rule_name][:rule_name], mapping ) disallowed_group_in_member?( trule, mapping ) elsif node[:group_rule] disallowed_group_in_member?( node[:group_rule], mapping ) end end
# File lib/jcr/check_groups.rb, line 140 def self.check_object_for_group node, mapping if node.is_a?( Array ) node.each do |child_node| check_object_for_group( child_node, mapping ) end elsif node.is_a? Hash if node[:target_rule_name] trule = get_name_mapping(node[:target_rule_name][:rule_name], mapping) disallowed_group_in_object?(trule, mapping) elsif node[:group_rule] disallowed_group_in_object?(node[:group_rule], mapping) end end end
# File lib/jcr/map_rule_names.rb, line 45 def self.check_rule_target_names( node, mapping ) if node.is_a? Array node.each do |child_node| check_rule_target_names( child_node, mapping ) end elsif node.is_a? Hash if node[:target_rule_name] && !mapping[ node[:target_rule_name][:rule_name].to_str ] raise_rule_name_missing node[:target_rule_name][:rule_name] else if node[:rule] check_rule_target_names( node[:rule], mapping ) elsif node[:group_rule] check_rule_target_names( node[:group_rule], mapping ) elsif node[:primitive_rule] check_rule_target_names( node[:primitive_rule], mapping ) elsif node[:array_rule] check_rule_target_names( node[:array_rule], mapping ) elsif node[:object_rule] check_rule_target_names( node[:object_rule], mapping ) elsif node[:member_rule] check_rule_target_names( node[:member_rule], mapping ) end end end end
# File lib/jcr/check_groups.rb, line 41 def self.check_value_for_group node, mapping if node.is_a?( Hash ) && node[:group_rule] disallowed_group_in_value?( node[:group_rule], mapping ) end end
# File lib/jcr/jcr.rb, line 385 def self.cli_eval ctx, data, root_name, quiet ec = 2 e = ctx.evaluate( data, root_name ) if e.success unless quiet puts "Success!" end ec = 0 else unless quiet puts "Failure! Use -v for more information." ctx.failure_report.each do |line| puts line end end ec = 3 end return ec end
# File lib/jcr/check_groups.rb, line 122 def self.disallowed_group_in_array? node, mapping if node.is_a? Hash node = [ node ] end node.each do |groupee| if groupee[:group_rule] disallowed_group_in_array?( groupee[:group_rule], mapping ) elsif groupee[:target_rule_name] trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping ) disallowed_group_in_array?( trule, mapping ) elsif groupee[:member_rule] raise_group_error( "groups in array rules cannot have member rules", groupee[:member_rule] ) else check_groups( groupee, mapping ) end end end
# File lib/jcr/check_groups.rb, line 84 def self.disallowed_group_in_member? node, mapping if node.is_a? Hash node = [ node ] end node.each do |groupee| if groupee[:sequence_combiner] raise_group_error( 'AND (comma) operation in group rule of member rule', groupee[:sequence_combiner] ) end if groupee[:group_rule] disallowed_group_in_member?( groupee[:group_rule], mapping ) elsif groupee[:target_rule_name] trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping ) if trule[:group_rule] disallowed_group_in_member?( trule[:group_rule], mapping ) end elsif groupee[:member_rule] raise_group_error( "groups in member rules cannot have member rules", groupee[:member_rule] ) else check_groups( groupee, mapping ) end end end
# File lib/jcr/check_groups.rb, line 155 def self.disallowed_group_in_object? node, mapping if node.is_a? Hash node = [ node ] end node.each do |groupee| if groupee[:group_rule] disallowed_group_in_object?( groupee[:group_rule], mapping ) elsif groupee[:target_rule_name] trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping ) disallowed_group_in_object?( trule, mapping ) elsif groupee[:array_rule] raise_group_error( "groups in object rules cannot have array rules", groupee[:member_rule] ) elsif groupee[:object_rule] raise_group_error( "groups in object rules cannot have other object rules", groupee[:member_rule] ) elsif groupee[:primitive_rule] raise_group_error( "groups in object rules cannot have value rules", groupee[:member_rule] ) else check_groups( groupee, mapping ) end end end
# File lib/jcr/check_groups.rb, line 47 def self.disallowed_group_in_value? node, mapping if node.is_a? Hash node = [ node ] end node.each do |groupee| if groupee[:sequence_combiner] raise_group_error( 'AND (comma) operation in group rule of value rule', groupee[:sequence_combiner] ) end if groupee[:group_rule] disallowed_group_in_value?( groupee[:group_rule], mapping ) elsif groupee[:target_rule_name] trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping ) disallowed_group_in_value?( trule[:rule], mapping ) elsif groupee[:member_rule] raise_group_error( "groups in value rules cannot have member rules", groupee[:member_rule] ) elsif groupee[:object_rule] raise_group_error( "groups in value rules cannot have object rules", groupee[:member_rule] ) elsif groupee[:array_rule] raise_group_error( "groups in value rules cannot have array rules", groupee[:member_rule] ) elsif groupee[:primitive_rule] disallowed_group_in_value?( groupee[:primitive_rule], mapping ) end end end
# File lib/jcr/evaluate_rules.rb, line 317 def self.elide s if s.length > 45 s = s[0..41] s = s + " ..." end return s end
# File lib/jcr/evaluate_array_rules.rb, line 69 def self.evaluate_array jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil rules, annotations = get_rules_and_annotations( jcr ) ordered = true if behavior && behavior.is_a?( ArrayBehavior ) ordered = behavior.ordered end annotations.each do |a| if a[:unordered_annotation] ordered = false break end end # if the data is not an array return evaluate_not( annotations, Evaluation.new( false, "#{data} is not an array #{raised_rule(jcr,rule_atom)}"), econs, target_annotations ) unless data.is_a? Array # if the array is zero length and there are zero sub-rules (it is suppose to be empty) return evaluate_not( annotations, Evaluation.new( true, nil ), econs, target_annotations ) if rules.empty? && data.empty? # if the array is not empty and there are zero sub-rules (it is suppose to be empty) return evaluate_not( annotations, Evaluation.new( false, "Non-empty array for #{raised_rule(jcr,rule_atom)}" ), econs, target_annotations ) if rules.empty? && data.length != 0 if ordered return evaluate_not( annotations, evaluate_array_rule_ordered( rules, rule_atom, data, econs, behavior ), econs, target_annotations ) else return evaluate_not( annotations, evaluate_array_rule_unordered( rules, rule_atom, data, econs, behavior ), econs, target_annotations ) end end
# File lib/jcr/evaluate_array_rules.rb, line 48 def self.evaluate_array_rule jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil push_trace_stack( econs, jcr ) if behavior trace( econs, "Evaluating group in array rule starting at #{slice_to_s(jcr)} against", data ) trace_def( econs, "array group", jcr, data ) else trace( econs, "Evaluating array rule starting at #{slice_to_s(jcr)} against", data ) trace_def( econs, "array", jcr, data ) end retval = evaluate_array( jcr, rule_atom, data, econs, behavior, target_annotations ) if behavior trace_eval( econs, "Array group", retval, jcr, data, "array" ) else trace_eval( econs, "Array", retval, jcr, data, "array" ) end pop_trace_stack( econs ) return retval end
# File lib/jcr/evaluate_array_rules.rb, line 109 def self.evaluate_array_rule_ordered jcr, rule_atom, data, econs, behavior = nil retval = nil behavior = ArrayBehavior.new unless behavior array_index = behavior.last_index jcr.each do |rule| # short circuit logic if rule[:choice_combiner] && retval && retval.success next elsif rule[:sequence_combiner] && retval && !retval.success break end repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs ) # group rules must be evaluated differently # groups require the effects of the evaluation to be discarded if they are false # groups must also be given the entire array grule, target_annotations = get_group( rule, econs ) if grule if repeat_min == 0 retval = Evaluation.new( true, nil ) else for i in 1..repeat_min do if array_index == data.length return Evaluation.new( false, "array is not large enough for #{raised_rule(jcr,rule_atom)}" ) else group_behavior = ArrayBehavior.new( behavior ) group_behavior.last_index = array_index retval = evaluate_rule( grule, rule_atom, data, econs, group_behavior, target_annotations ) if retval.success behavior.checked_hash.merge!( group_behavior.checked_hash ) array_index = group_behavior.last_index else break; end end end end if !retval || retval.success for i in behavior.checked_hash.length..repeat_max-1 do break if array_index == data.length group_behavior = ArrayBehavior.new( behavior ) group_behavior.last_index = array_index e = evaluate_rule( grule, rule_atom, data, econs, group_behavior, target_annotations ) if e.success behavior.checked_hash.merge!( group_behavior.checked_hash ) array_index = group_behavior.last_index else break; end end end else # else not grule (group) if repeat_min == 0 retval = Evaluation.new( true, nil ) else for i in 1..repeat_min do if array_index == data.length return Evaluation.new( false, "array is not large enough for #{raised_rule(jcr,rule_atom)}" ) else retval = evaluate_rule( rule, rule_atom, data[ array_index ], econs, nil ) break unless retval.success array_index = array_index + 1 behavior.checked_hash[ i + behavior.last_index ] = retval.success end end end if !retval || retval.success for i in behavior.checked_hash.length..repeat_max-1 do break if array_index == data.length e = evaluate_rule( rule, rule_atom, data[ array_index ], econs, nil ) break unless e.success array_index = array_index + 1 end end end # end if grule else if repeat_step && ( array_index - repeat_min ) % repeat_step != 0 retval = Evaluation.new( false, "Matches (#{array_index }) do not meat repetition step for #{repeat_max} % #{repeat_step}") end end behavior.last_index = array_index if data.length > array_index && behavior.extra_prohibited retval = Evaluation.new( false, "More items in array (#{data.length}) than specified (#{array_index}) for #{raised_rule(jcr,rule_atom)}" ) end return retval end
# File lib/jcr/evaluate_array_rules.rb, line 211 def self.evaluate_array_rule_unordered jcr, rule_atom, data, econs, behavior = nil retval = nil unless behavior behavior = ArrayBehavior.new behavior.ordered = false end highest_index = 0 jcr.each do |rule| # short circuit logic if rule[:choice_combiner] && retval && retval.success next elsif rule[:sequence_combiner] && retval && !retval.success break end repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs ) # group rules must be evaluated differently # groups require the effects of the evaluation to be discarded if they are false # groups must also be given the entire array grule,target_annotations = get_group(rule, econs) if grule successes = 0 for i in 0..repeat_max-1 group_behavior = ArrayBehavior.new( behavior ) group_behavior.last_index = highest_index group_behavior.ordered = false e = evaluate_rule( grule, rule_atom, data, econs, group_behavior, target_annotations ) if e.success highest_index = group_behavior.last_index behavior.checked_hash.merge!( group_behavior.checked_hash ) successes = successes + 1 else break; end end if successes == 0 && repeat_min > 0 retval = Evaluation.new( false, "array does not contain #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif successes < repeat_min retval = Evaluation.new( false, "array does not have enough #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif successes > repeat_max retval = Evaluation.new( false, "array has too many #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif repeat_step && ( successes - repeat_min ) % repeat_step != 0 retval = Evaluation.new( false, "array matches (#{successes}) do not meet repetition step of #{repeat_max} % #{repeat_step} with #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") else retval = Evaluation.new( true, nil ) end else # else not group rule successes = 0 for i in behavior.last_index..data.length-1 break if successes == repeat_max unless behavior.checked_hash[ i ] e = evaluate_rule( rule, rule_atom, data[ i ], econs, nil ) if e.success behavior.checked_hash[ i ] = e.success highest_index = i if i > highest_index successes = successes + 1 end end end if successes == 0 && repeat_min > 0 retval = Evaluation.new( false, "array does not contain #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif successes < repeat_min retval = Evaluation.new( false, "array does not have enough #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif successes > repeat_max retval = Evaluation.new( false, "array has too many #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif repeat_step && ( successes - repeat_min ) % repeat_step != 0 retval = Evaluation.new( false, "array matches (#{successes}) do not meet repetition step of #{repeat_max} % #{repeat_step} with #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") else retval = Evaluation.new( true, nil) end end # if grule else end behavior.last_index = highest_index if data.length > behavior.checked_hash.length && behavior.extra_prohibited retval = Evaluation.new( false, "More items in array #{data.length} than specified #{behavior.checked_hash.length} for #{raised_rule(jcr,rule_atom)}" ) end return retval end
# File lib/jcr/evaluate_rules.rb, line 146 def self.evaluate_callback jcr, data, econs, callback, e retval = e c = econs.callbacks[ callback ] if e.success retval = c.jcr_callback :rule_eval_true, jcr, data else retval = c.jcr_callback :rule_eval_false, jcr, data, e end if retval.is_a? TrueClass retval = Evaluation.new( true, nil ) elsif retval.is_a? FalseClass retval = Evaluation.new( false, nil ) elsif retval.is_a? String retval = Evaluation.new( false, retval ) end trace( econs, "Callback #{callback} given evaluation of #{e.success} and returned #{retval}") return retval end
# File lib/jcr/evaluate_group_rules.rb, line 41 def self.evaluate_group jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil rules, annotations = get_rules_and_annotations( jcr ) retval = nil rules.each do |rule| if rule[:choice_combiner] && retval && retval.success return evaluate_not( annotations, retval, econs, target_annotations ) # short circuit elsif rule[:sequence_combiner] && retval && !retval.success return evaluate_not( annotations, retval, econs, target_annotations ) # short circuit end retval = evaluate_rule( rule, rule_atom, data, econs, behavior ) end return evaluate_not( annotations, retval, econs, target_annotations ) end
# File lib/jcr/evaluate_group_rules.rb, line 29 def self.evaluate_group_rule jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil push_trace_stack( econs, jcr ) trace( econs, "Evaluating group rule against ", data ) trace_def( econs, "group", jcr, data ) retval = evaluate_group( jcr, rule_atom, data, econs, behavior, target_annotations ) trace_eval( econs, "Group", retval, jcr, data, "group" ) pop_trace_stack( econs ) return retval end
# File lib/jcr/evaluate_member_rules.rb, line 40 def self.evaluate_member jcr, rule_atom, data, econs, behavior, target_annotations # unlike the other evaluate functions, here data is not just the json data. # it is an array, the first element being the member name or regex and the # second being the json data to be furthered on to other evaluation functions rules, annotations = get_rules_and_annotations( jcr ) rule = merge_rules( rules ) member_match = false if rule[:member_name] match_spec = rule[:member_name][:q_string].to_s if match_spec == data[ 0 ] member_match = true end else # must be regex regex = rule[:member_regex][:regex] if regex.is_a? Array match_spec = Regexp.new( "" ) trace( econs, "Noting empty regular expression." ) else match_spec = Regexp.new( rule[:member_regex][:regex].to_s ) end if match_spec =~ data[ 0 ] member_match = true end end if member_match e = evaluate_rule( rule, rule_atom, data[ 1 ], econs, nil, target_annotations ) e.member_found = true return evaluate_not( annotations, e, econs, target_annotations ) end return evaluate_not( annotations, Evaluation.new( false, "#{match_spec} does not match #{data[0]} for #{raised_rule( jcr, rule_atom)}" ), econs, target_annotations ) end
# File lib/jcr/evaluate_member_rules.rb, line 28 def self.evaluate_member_rule jcr, rule_atom, data, econs, behavior, target_annotations push_trace_stack( econs, jcr ) trace( econs, "Evaluating member rule for key '#{data[0]}' starting at #{slice_to_s(jcr)} against ", data[1]) trace_def( econs, "member", jcr, data ) retval = evaluate_member( jcr, rule_atom, data, econs, behavior, target_annotations ) trace_eval( econs, "Member", retval, jcr, data, "member" ) pop_trace_stack( econs ) return retval end
# File lib/jcr/evaluate_rules.rb, line 264 def self.evaluate_not annotations, evaluation, econs, target_annotations = nil is_not = false target_annotations.each do |a| if a[:not_annotation] trace( econs, "Not annotation found on reference to rule.") is_not = !is_not break end end if target_annotations annotations.each do |a| if a[:not_annotation] is_not = !is_not break end end if is_not trace( econs, "Not annotation changing result from #{evaluation.success} to #{!evaluation.success}") evaluation.success = !evaluation.success end return evaluation end
# File lib/jcr/evaluate_object_rules.rb, line 51 def self.evaluate_object jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil rules, annotations = get_rules_and_annotations( jcr ) # if the data is not an object (Hash) return evaluate_not( annotations, Evaluation.new( false, "#{data} is not an object for #{raised_rule(jcr,rule_atom)}"), econs, target_annotations ) unless data.is_a? Hash # if the object has no members and there are zero sub-rules (it is suppose to be empty) return evaluate_not( annotations, Evaluation.new( true, nil ), econs, target_annotations ) if rules.empty? && data.length == 0 # if the object has members and there are zero sub-rules (it is suppose to be empty) return evaluate_not( annotations, Evaluation.new( false, "Non-empty object for #{raised_rule(jcr,rule_atom)}" ), econs, target_annotations ) if rules.empty? && data.length != 0 retval = nil behavior = ObjectBehavior.new unless behavior rules.each do |rule| # short circuit logic if rule[:choice_combiner] && retval && retval.success next elsif rule[:sequence_combiner] && retval && !retval.success return evaluate_not( annotations, retval, econs, target_annotations ) # short circuit end repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs ) # Pay attention here: # Group rules need to be treated differently than other rules # Groups must be evaluated as if they are rules evaluated in # isolation until they evaluate as true. # Also, groups must be handed the entire object, not key/values # as member rules use. grule,gtarget_annotations = get_group(rule, econs) if grule successes = 0 for i in 0..repeat_max-1 group_behavior = ObjectBehavior.new group_behavior.checked_hash.merge!( behavior.checked_hash ) e = evaluate_rule( grule, rule_atom, data, econs, group_behavior, gtarget_annotations ) if e.success behavior.checked_hash.merge!( group_behavior.checked_hash ) successes = successes + 1 else break; end end if successes == 0 && repeat_min > 0 retval = Evaluation.new( false, "object does not contain group #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif successes < repeat_min retval = Evaluation.new( false, "object does not have contain necessary number of group #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif repeat_step && ( successes - repeat_min ) % repeat_step != 0 retval = Evaluation.new( false, "object matches (#{successes}) do not have contain repetition #{repeat_max} % #{repeat_step} of group #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") else retval = Evaluation.new( true, nil ) end else # if not grule repeat_results = nil member_found = false # do a little lookahead for member rules defined by names # if defined by a name, and not a regex, just pluck it from the object # and short-circuit the enumeration lookahead, ltarget_annotations = get_leaf_rule( rule, econs ) lrules, lannotations = get_rules_and_annotations( lookahead[:member_rule] ) if lrules[0][:member_name] repeat_results = {} k = lrules[0][:member_name][:q_string].to_s v = data[k] if v unless behavior.checked_hash[k] e = evaluate_rule(rule, rule_atom, [k, v], econs, nil, nil) behavior.checked_hash[k] = e.success member_found = true if e.member_found repeat_results[ k ] = v if e.success end else trace( econs, "No member '#{k}' found in object.") e = evaluate_rule(rule, rule_atom, [nil, nil], econs, nil, nil) repeat_results[ nil ] = nil if e.success end else regex = lrules[0][:member_regex][:regex] trace( econs, "Scanning object for #{regex}.") i = 0 found = false repeat_results = data.select do |k,v| unless behavior.checked_hash[k] if i < repeat_max e = evaluate_rule(rule, rule_atom, [k, v], econs, nil, nil) behavior.checked_hash[k] = e.success i = i + 1 if e.success found = true if e.member_found member_found = true if e.member_found e.success end end end unless found trace( econs, "No member matching #{regex} found in object.") e = evaluate_rule(rule, rule_atom, [nil, nil], econs, nil, nil) repeat_results[ nil ] = nil if e.success end end trace( econs, "Found #{repeat_results.length} matching members repetitions in object with min #{repeat_min} and max #{repeat_max}" ) if repeat_results.length == 0 && repeat_min > 0 retval = Evaluation.new( false, "object does not contain #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif repeat_results.length < repeat_min retval = Evaluation.new( false, "object does not have enough #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif repeat_results.length > repeat_max retval = Evaluation.new( false, "object has too many #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif repeat_step && ( repeat_results.length - repeat_min ) % repeat_step != 0 retval = Evaluation.new( false, "object matches (#{repeat_results.length}) does not match repetition step of #{repeat_max} & #{repeat_step} for #{jcr_to_s(rule)} for #{raised_rule(jcr,rule_atom)}") elsif member_found && repeat_results.length == 0 && repeat_max > 0 retval = Evaluation.new( false, "object contains #{jcr_to_s(rule)} with member name though incorrect value for #{raised_rule(jcr,rule_atom)}") else retval = Evaluation.new( true, nil) end end end # end if grule else return evaluate_not( annotations, retval, econs, target_annotations ) end
# File lib/jcr/evaluate_object_rules.rb, line 30 def self.evaluate_object_rule jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil push_trace_stack( econs, jcr ) if behavior trace( econs, "Evaluating group in object rule starting at #{slice_to_s(jcr)} against", data ) trace_def( econs, "object group", jcr, data ) else trace( econs, "Evaluating object rule starting at #{slice_to_s(jcr)} against", data ) trace_def( econs, "object", jcr, data ) end retval = evaluate_object( jcr, rule_atom, data, econs, behavior, target_annotations ) if behavior trace_eval( econs, "Object group", retval, jcr, data, "object" ) else trace_eval( econs, "Object", retval, jcr, data, "object" ) end pop_trace_stack( econs ) return retval end
# File lib/jcr/evaluate_rules.rb, line 104 def self.evaluate_rule jcr, rule_atom, data, econs, behavior = nil, target_annotations = nil trace( econs, "Dispatching rule for #{jcr_to_s(jcr)} with data: #{data}") if jcr.is_a?( Hash ) if jcr[:rule_name] rn = slice_to_s( jcr[:rule_name] ) trace( econs, "Named Rule: #{rn}" ) end end retval = Evaluation.new( false, "failed to evaluate rule properly" ) case when behavior.is_a?( ArrayBehavior ) retval = evaluate_array_rule( jcr, rule_atom, data, econs, behavior, target_annotations ) when behavior.is_a?( ObjectBehavior ) retval = evaluate_object_rule( jcr, rule_atom, data, econs, behavior, target_annotations ) when jcr[:rule] retval = evaluate_rule( jcr[:rule], rule_atom, data, econs, behavior, target_annotations) when jcr[:target_rule_name] target, target_annotations = get_target_rule( jcr, econs ) retval = evaluate_rule( target, target, data, econs, behavior, target_annotations ) when jcr[:primitive_rule] retval = evaluate_value_rule( jcr[:primitive_rule], rule_atom, data, econs, nil, target_annotations ) when jcr[:group_rule] retval = evaluate_group_rule( jcr[:group_rule], rule_atom, data, econs, behavior, target_annotations) when jcr[:array_rule] retval = evaluate_array_rule( jcr[:array_rule], rule_atom, data, econs, behavior, target_annotations ) when jcr[:object_rule] retval = evaluate_object_rule( jcr[:object_rule], rule_atom, data, econs, behavior, target_annotations) when jcr[:member_rule] retval = evaluate_member_rule( jcr[:member_rule], rule_atom, data, econs, nil, target_annotations) else retval = Evaluation.new( true, nil ) end if jcr.is_a?( Hash ) && jcr[:rule_name] rn = jcr[:rule_name].to_s if econs.callbacks[ rn ] retval = evaluate_callback( jcr, data, econs, rn, retval ) end end return retval end
# File lib/jcr/jcr.rb, line 119 def self.evaluate_ruleset( data, ctx, root_name = nil ) roots = [] if root_name root_rule = ctx.mapping[root_name] raise JcrValidatorError, "No rule by the name of #{root_name} for a root rule has been found" unless root_rule root = JCR::Root.new( root_rule, root_name ) roots << root else roots = ctx.roots end raise JcrValidatorError, "No root rule defined. Specify a root rule name" if roots.empty? retval = nil roots.each do |r| pp "Evaluating Root:", rule_to_s( r.rule, false ) if ctx.trace raise JcrValidatorError, "Root rules cannot be member rules" if r.rule[:member_rule] econs = EvalConditions.new( ctx.mapping, ctx.callbacks, ctx.trace ) retval = JCR.evaluate_rule( r.rule, r.rule, data, econs ) break if retval.success # else r.failures = econs.failures ctx.failed_roots << r end ctx.failure_report = failure_report( ctx ) return retval end
# File lib/jcr/evaluate_value_rules.rb, line 28 def self.evaluate_value_rule jcr, rule_atom, data, econs, behavior, target_annotations push_trace_stack( econs, jcr ) trace( econs, "Evaluating value rule starting at #{slice_to_s(jcr)}" ) trace_def( econs, "value", jcr, data ) rules, annotations = get_rules_and_annotations( jcr ) retval = evaluate_not( annotations, evaluate_values( rules[0], rule_atom, data, econs ), econs, target_annotations ) trace_eval( econs, "Value", retval, jcr, data, "value") pop_trace_stack( econs ) return retval end
# File lib/jcr/evaluate_value_rules.rb, line 42 def self.evaluate_values jcr, rule_atom, data, econs case # # any # when jcr[:any] return Evaluation.new( true, nil ) # # integers # when jcr[:integer_v] si = jcr[:integer_v].to_s if si == "integer" return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer ) end when jcr[:integer] i = jcr[:integer].to_s.to_i return bad_value( jcr, rule_atom, i, data ) unless data == i when jcr[:integer_min] != nil && jcr[:integer_max] == nil return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer ) min = jcr[:integer_min].to_s.to_i return bad_value( jcr, rule_atom, min, data ) unless data >= min when jcr[:integer_min] == nil && jcr[:integer_max] != nil return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer ) max = jcr[:integer_max].to_s.to_i return bad_value( jcr, rule_atom, max, data ) unless data <= max when jcr[:integer_min],jcr[:integer_max] return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer ) min = jcr[:integer_min].to_s.to_i return bad_value( jcr, rule_atom, min, data ) unless data >= min max = jcr[:integer_max].to_s.to_i return bad_value( jcr, rule_atom, max, data ) unless data <= max when jcr[:sized_int_v] bits = jcr[:sized_int_v][:bits].to_i return bad_value( jcr, rule_atom, "int" + bits.to_s, data ) unless data.is_a?( Integer ) min = -(2**(bits-1)) return bad_value( jcr, rule_atom, min, data ) unless data >= min max = 2**(bits-1)-1 return bad_value( jcr, rule_atom, max, data ) unless data <= max when jcr[:sized_uint_v] bits = jcr[:sized_uint_v][:bits].to_i return bad_value( jcr, rule_atom, "int" + bits.to_s, data ) unless data.is_a?( Integer ) min = 0 return bad_value( jcr, rule_atom, min, data ) unless data >= min max = 2**bits-1 return bad_value( jcr, rule_atom, max, data ) unless data <= max # # floats # when jcr[:float_v] sf = jcr[:float_v].to_s if sf == "float" return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float ) end when jcr[:float] f = jcr[:float].to_s.to_f return bad_value( jcr, rule_atom, f, data ) unless data == f when jcr[:float_min] != nil && jcr[:float_max] == nil return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float ) min = jcr[:float_min].to_s.to_f return bad_value( jcr, rule_atom, min, data ) unless data >= min when jcr[:float_min] == nil && jcr[:float_max] != nil return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float ) max = jcr[:float_max].to_s.to_f return bad_value( jcr, rule_atom, max, data ) unless data <= max when jcr[:float_min],jcr[:float_max] return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float ) min = jcr[:float_min].to_s.to_f return bad_value( jcr, rule_atom, min, data ) unless data >= min max = jcr[:float_max].to_s.to_f return bad_value( jcr, rule_atom, max, data ) unless data <= max when jcr[:double_v] sf = jcr[:double_v].to_s if sf == "double" return bad_value( jcr, rule_atom, "double", data ) unless data.is_a?( Float ) end # # boolean # when jcr[:true_v] return bad_value( jcr, rule_atom, "true", data ) unless data when jcr[:false_v] return bad_value( jcr, rule_atom, "false", data ) if data when jcr[:boolean_v] return bad_value( jcr, rule_atom, "boolean", data ) unless ( data.is_a?( TrueClass ) || data.is_a?( FalseClass ) ) # # strings # when jcr[:string] return bad_value( jcr, rule_atom, "string", data ) unless data.is_a? String when jcr[:q_string] s = jcr[:q_string].to_s return bad_value( jcr, rule_atom, s, data ) unless data == s # # regex # when jcr[:regex] regex = Regexp.new( jcr[:regex].to_s ) return bad_value( jcr, rule_atom, regex, data ) unless data.is_a? String return bad_value( jcr, rule_atom, regex, data ) unless data =~ regex # # ip addresses # when jcr[:ipv4] return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless data.is_a? String begin ip = IPAddr.new( data ) rescue IPAddr::InvalidAddressError return bad_value( jcr, rule_atom, "IPv4 Address", data ) end return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless ip.ipv4? when jcr[:ipv6] return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless data.is_a? String begin ip = IPAddr.new( data ) rescue IPAddr::InvalidAddressError return bad_value( jcr, rule_atom, "IPv6 Address", data ) end return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless ip.ipv6? when jcr[:ipaddr] return bad_value( jcr, rule_atom, "IP Address", data ) unless data.is_a? String begin ip = IPAddr.new( data ) rescue IPAddr::InvalidAddressError return bad_value( jcr, rule_atom, "IP Address", data ) end return bad_value( jcr, rule_atom, "IP Address", data ) unless ip.ipv6? || ip.ipv4? # # domain names # when jcr[:fqdn] return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) unless data.is_a? String return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if data.empty? a = data.split( '.' ) a.each do |label| return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if label.start_with?( '-' ) return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if label.end_with?( '-' ) label.each_char do |char| unless (char >= 'a' && char <= 'z') \ || (char >= 'A' && char <= 'Z') \ || (char >= '0' && char <='9') \ || char == '-' return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) end end end when jcr[:idn] return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) unless data.is_a? String return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if data.empty? a = data.split( '.' ) a.each do |label| return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if label.start_with?( '-' ) return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if label.end_with?( '-' ) label.each_char do |char| unless (char >= 'a' && char <= 'z') \ || (char >= 'A' && char <= 'Z') \ || (char >= '0' && char <='9') \ || char == '-' \ || char.ord > 127 return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) end end end # # uri and uri scheme # when jcr[:uri] if jcr[:uri].is_a? Hash t = jcr[:uri][:uri_scheme].to_s return bad_value( jcr, rule_atom, t, data ) unless data.is_a? String return bad_value( jcr, rule_atom, t, data ) unless data.start_with?( t ) else return bad_value( jcr, rule_atom, "URI", data ) unless data.is_a?( String ) uri = Addressable::URI.parse( data ) return bad_value( jcr, rule_atom, "URI", data ) unless uri.is_a?( Addressable::URI ) end # # phone and email value rules # when jcr[:email] return bad_value( jcr, rule_atom, "Email Address", data ) unless data.is_a? String return bad_value( jcr, rule_atom, "Email Address", data ) unless EmailAddressValidator.validate( data, true ) when jcr[:phone] return bad_value( jcr, rule_atom, "Phone Number", data ) unless data.is_a? String p = BigPhoney::PhoneNumber.new( data ) return bad_value( jcr, rule_atom, "Phone Number", data ) unless p.valid? # # hex values # when jcr[:hex] return bad_value( jcr, rule_atom, "Hex Data", data ) unless data.is_a? String return bad_value( jcr, rule_atom, "Hex Data", data ) unless data.length % 2 == 0 pad_start = false data.each_char do |char| unless (char >= '0' && char <='9') \ || (char >= 'A' && char <= 'F') \ || (char >= 'a' && char <= 'f') return bad_value( jcr, rule_atom, "Hex Data", data ) end end # # base32hex values # when jcr[:base32hex] return bad_value( jcr, rule_atom, "Base32hex Data", data ) unless data.is_a? String return bad_value( jcr, rule_atom, "Base32hex Data", data ) unless data.length % 8 == 0 pad_start = false data.each_char do |char| if char == '=' pad_start = true elsif pad_start && char != '=' return bad_value( jcr, rule_atom, "Base32hex Data", data ) else unless (char >= '0' && char <='9') \ || (char >= 'A' && char <= 'V') \ || (char >= 'a' && char <= 'v') return bad_value( jcr, rule_atom, "Base32hex Data", data ) end end end # # base32 values # when jcr[:base32] return bad_value( jcr, rule_atom, "Base 32 Data", data ) unless data.is_a? String return bad_value( jcr, rule_atom, "Base 32 Data", data ) unless data.length % 8 == 0 pad_start = false data.each_char do |char| if char == '=' pad_start = true elsif pad_start && char != '=' return bad_value( jcr, rule_atom, "Base 32 Data", data ) else unless (char >= 'a' && char <= 'z') \ || (char >= 'A' && char <= 'Z') \ || (char >= '2' && char <='7') return bad_value( jcr, rule_atom, "Base 32 Data", data ) end end end # # base64url values # when jcr[:base64url] return bad_value( jcr, rule_atom, "Base64url Data", data ) unless data.is_a? String return bad_value( jcr, rule_atom, "Base64url Data", data ) unless data.length % 4 == 0 pad_start = false data.each_char do |char| if char == '=' pad_start = true elsif pad_start && char != '=' return bad_value( jcr, rule_atom, "Base64url Data", data ) else unless (char >= 'a' && char <= 'z') \ || (char >= 'A' && char <= 'Z') \ || (char >= '0' && char <='9') \ || char == '-' || char == '_' return bad_value( jcr, rule_atom, "Base64url Data", data ) end end end # # base64 values # when jcr[:base64] return bad_value( jcr, rule_atom, "Base 64 Data", data ) unless data.is_a? String return bad_value( jcr, rule_atom, "Base 64 Data", data ) unless data.length % 4 == 0 pad_start = false data.each_char do |char| if char == '=' pad_start = true elsif pad_start && char != '=' return bad_value( jcr, rule_atom, "Base 64 Data", data ) else unless (char >= 'a' && char <= 'z') \ || (char >= 'A' && char <= 'Z') \ || (char >= '0' && char <='9') \ || char == '+' || char == '/' return bad_value( jcr, rule_atom, "Base 64 Data", data ) end end end # # time and date values # when jcr[:datetime] return bad_value( jcr, rule_atom, "Time and Date", data ) unless data.is_a? String begin Time.iso8601( data ) rescue ArgumentError return bad_value( jcr, rule_atom, "Time and Date", data ) end when jcr[:date] return bad_value( jcr, rule_atom, "Date", data ) unless data.is_a? String begin d = data + "T23:20:50.52Z" Time.iso8601( d ) rescue ArgumentError return bad_value( jcr, rule_atom, "Date", data ) end when jcr[:time] return bad_value( jcr, rule_atom, "Time", data ) unless data.is_a? String begin t = "1985-04-12T" + data + "Z" Time.iso8601( t ) rescue ArgumentError return bad_value( jcr, rule_atom, "Time", data ) end # # null # when jcr[:null] return bad_value( jcr, rule_atom, nil, data ) unless data == nil # # groups # when jcr[:group_rule] return evaluate_group_rule jcr[:group_rule], rule_atom, data, econs else raise "unknown value rule evaluation. this shouldn't happen" end return Evaluation.new( true, nil ) end
# File lib/jcr/jcr.rb, line 148 def self.failure_report ctx report = [] ctx.failed_roots.each do |failed_root| if failed_root.name report << "- Failures for root rule named '#{failed_root.name}'" else report << "- Failures for root rule at line #{failed_root.pos[0]}" end failed_root.failures.each_with_index do |failures,stack_level| if failures.length > 1 report << " - failure at rule level #{stack_level} caused by one of the following #{failures.length} reasons" else report << " - failure at rule level #{stack_level} caused by" end failures.each_with_index do |failure, index| lines = breakup_message( "<< #{failure.json_elided} >> failed rule #{failure.definition}", ctx.failure_report_line_length - 5 ) lines.each_with_index do |l,i| if i == 0 report << " - #{l}" else report << " #{l}" end end end end end return report end
# File lib/jcr/evaluate_rules.rb, line 413 def self.find_first_slice slice if slice.is_a? Parslet::Slice return slice elsif slice.is_a?( Hash ) && !slice.empty? s = nil slice.values.each do |v| s = find_first_slice( v ) break if s end return s if s elsif slice.is_a?( Array ) && !slice.empty? s = nil slice.each do |i| s = find_first_slice( i ) break if s end return s if s end #else return nil end
# File lib/jcr/find_roots.rb, line 37 def self.find_roots( tree ) roots = Array.new if tree.is_a? Hash tree = [ tree ] end tree.each do |node| if node[:rule] roots.concat( find_roots_in_named( node ) ) elsif (top_rule = get_rule_by_type( node )) roots << Root.new( node, nil, true, true ) roots.concat( find_roots_in_unnamed( top_rule ) ) end end return roots end
# File lib/jcr/find_roots.rb, line 53 def self.find_roots_in_named( node ) roots = Array.new rn = node[:rule][:rule_name].to_str rule = node[:rule] ruledef = get_rule_by_type( rule ) new_root = nil # look to see if the root_annotation is in the name before assignment ( @{root} $r = ... ) if rule[:annotations] if rule[:annotations].is_a? Array rule[:annotations].each do |annotation| if annotation[:root_annotation] new_root = Root.new(node, rn) roots << new_root # root is found, now look into subrule for unnamed roots subrule = get_rule_by_type( ruledef ) roots.concat( find_roots_in_unnamed( subrule ) ) if subrule end end elsif rule[:annotations][:root_annotation] new_root = Root.new(node, rn) roots << new_root # root is found, now look into subrule for unnamed roots subrule = get_rule_by_type( ruledef ) roots.concat( find_roots_in_unnamed( subrule ) ) if subrule end end if ruledef && !new_root if ruledef.is_a? Array ruledef.each do |rdi| # if it has a @{root} annotation in the rule definition if rdi[:root_annotation] roots << Root.new(node, rn) # else look into the definition further and examine subrules elsif (subrule = get_rule_by_type(rdi)) roots.concat(find_roots_in_unnamed(subrule)) end end elsif ruledef.is_a? Hash subrule = get_rule_by_type(ruledef) roots.concat(find_roots_in_unnamed(subrule)) if subrule end end return roots end
# File lib/jcr/find_roots.rb, line 98 def self.find_roots_in_unnamed( node ) roots = Array.new if node.is_a? Array node.each do |n| if n[:root_annotation] roots << Root.new( node ) elsif (subrule = get_rule_by_type( n ) ) roots.concat( find_roots_in_unnamed( subrule ) ) if subrule end end else subrule = get_rule_by_type( node ) roots.concat( find_roots_in_unnamed( subrule ) ) if subrule end return roots end
# File lib/jcr/evaluate_rules.rb, line 289 def self.get_group rule, econs return rule[:group_rule], nil if rule[:group_rule] #else if rule[:target_rule_name] target, target_annotations = get_target_rule( rule, econs ) return get_group( target, econs )[0], target_annotations end #else return false, nil end
# File lib/jcr/evaluate_rules.rb, line 300 def self.get_leaf_rule rule, econs if rule[:target_rule_name ] target, target_annotations = get_target_rule( rule, econs ) return target, target_annotations end #else return rule, nil end
# File lib/jcr/map_rule_names.rb, line 71 def self.get_name_mapping rule_name, mapping trule = mapping[ rule_name.to_str ] raise_rule_name_missing( rule_name ) unless trule return trule end
# File lib/jcr/evaluate_rules.rb, line 172 def self.get_repetitions rule, econs repeat_min = 1 repeat_max = 1 repeat_step = nil if rule[:optional] repeat_min = 0 repeat_max = 1 elsif rule[:one_or_more] repeat_min = 1 repeat_max = Float::INFINITY if rule[:repetition_step] repeat_step = rule[:repetition_step].to_s.to_i repeat_min = repeat_step end elsif rule[:zero_or_more] repeat_min = 0 repeat_max = Float::INFINITY repeat_step = rule[:repetition_step].to_s.to_i if rule[:repetition_step] elsif rule[:specific_repetition] && rule[:specific_repetition].is_a?( Parslet::Slice ) repeat_min = repeat_max = rule[:specific_repetition].to_s.to_i else o = rule[:repetition_interval] if o repeat_min = 0 repeat_max = Float::INFINITY end o = rule[:repetition_min] if o if o.is_a?( Parslet::Slice ) repeat_min = o.to_s.to_i end end o = rule[:repetition_max] if o if o.is_a?( Parslet::Slice ) repeat_max = o.to_s.to_i end end o = rule[:repetition_step] if o if o.is_a?( Parslet::Slice ) repeat_step = o.to_s.to_i end end end trace( econs, "rule repetition min = #{repeat_min} max = #{repeat_max} repetition step = #{repeat_step}" ) return repeat_min, repeat_max, repeat_step end
# File lib/jcr/find_roots.rb, line 115 def self.get_rule_by_type rule retval = nil return retval unless rule.is_a? Hash case when rule[:array_rule] retval = rule[:array_rule] when rule[:object_rule] retval = rule[:object_rule] when rule[:member_rule] retval = rule[:member_rule] when rule[:primitive_rule] retval = rule[:primitive_rule] when rule[:group_rule] retval = rule[:group_rule] end return retval end
# File lib/jcr/evaluate_rules.rb, line 223 def self.get_rules_and_annotations jcr rules = [] annotations = [] if jcr.is_a?( Hash ) jcr = [ jcr ] end if jcr.is_a? Array i = 0 jcr.each do |sub| case when sub[:unordered_annotation] annotations << sub i = i + 1 when sub[:not_annotation] annotations << sub i = i + 1 when sub[:root_annotation] annotations << sub i = i + 1 when sub[:primitive_rule],sub[:object_rule],sub[:group_rule],sub[:array_rule],sub[:target_rule_name] break end end rules = jcr[i,jcr.length] end return rules, annotations end
# File lib/jcr/evaluate_rules.rb, line 165 def self.get_target_rule jcr, econs target = econs.mapping[ jcr[:target_rule_name][:rule_name].to_s ] raise "Target rule not in mapping. This should have been checked earlier." unless target trace( econs, "Referencing target rule #{slice_to_s(target)} from #{slice_to_s( jcr[:target_rule_name][:rule_name] )}" ) return target,jcr[:target_rule_name][:annotations] end
# File lib/jcr/evaluate_group_rules.rb, line 59 def self.group_to_s( jcr, shallow=true) rules, annotations = get_rules_and_annotations( jcr ) return "#{annotations_to_s( annotations)}( #{rules_to_s(rules,shallow)} )" end
# File lib/jcr/jcr.rb, line 101 def self.ingest_ruleset( ruleset, existing_mapping = nil, ruleset_alias=nil ) tree = JCR.parse( ruleset ) mapping = JCR.map_rule_names( tree, ruleset_alias ) combined_mapping = {} combined_mapping.merge!( existing_mapping ) if existing_mapping combined_mapping.merge!( mapping ) JCR.check_rule_target_names( tree, combined_mapping ) JCR.check_groups( tree, combined_mapping ) roots = JCR.find_roots( tree ) ctx = Context.new ctx.tree = tree ctx.mapping = mapping ctx.callbacks = {} ctx.roots = roots JCR.process_directives( ctx ) return ctx end
# File lib/jcr/evaluate_rules.rb, line 450 def self.jcr_to_s( jcr, shallow=true ) if jcr.is_a? Array retval = "" jcr.each_with_index do |item,idx| if idx > 1 retval = retval + " , " end retval = retval + jcr_to_s( item, shallow ) end elsif jcr.is_a? Parslet::Slice retval = slice_to_s( jcr ) else if jcr[:q_string] retval = value_to_s( jcr ) else retval = rule_to_s( jcr, shallow ) end end return "<< " + retval + " >>" end
# File lib/jcr/jcr.rb, line 186 def self.main my_argv=nil my_argv = ARGV unless my_argv options = {} opt_parser = OptionParser.new do |opt| opt.banner = "Usage: jcr [OPTIONS] [JSON_FILES]" opt.separator "" opt.separator "Evaluates JSON against JSON Content Rules (JCR)." opt.separator "" opt.separator "If -J is not specified, JSON_FILES is used." opt.separator "If JSON_FILES is not specified, standard input (STDIN) is used." opt.separator "" opt.separator "Use -v to see results, otherwise check the exit code." opt.separator "" opt.separator "Options" opt.on("-r FILE","file containing ruleset") do |ruleset| if options[:ruleset] puts "A ruleset has already been specified. Use -h for help.", "" return 2 end options[:ruleset] = File.open( ruleset ).read end opt.on("-R STRING","string containing ruleset. Should probably be quoted") do |ruleset| if options[:ruleset] puts "A ruleset has already been specified. Use -h for help.", "" return 2 end options[:ruleset] = ruleset end opt.on("--test-jcr", "parse and test the JCR only") do |testjcr| options[:testjcr] = true end opt.on("--process-parts [DIRECTORY]", "creates smaller files for specification writing" ) do |directory| options[:process_parts] = true options[:process_parts_directory] = directory end opt.on("-S STRING","name of root rule. All roots will be tried if none is specified") do |root_name| if options[:root_name] puts "A root has already been specified. Use -h for help.", "" return 2 end options[:root_name] = root_name end opt.on("-o FILE","file containing overide ruleset (option can be repeated)") do |ruleset| unless options[:overrides] options[:overrides] = Array.new end options[:overrides] << File.open( ruleset ).read end opt.on("-O STRING","string containing overide rule (option can be repeated)") do |rule| unless options[:overrides] options[:overrides] = Array.new end options[:overrides] << rule end opt.on("-J STRING","string containing JSON to evaluate. Should probably be quoted") do |json| if options[:json] puts "JSON has already been specified. Use -h for help.", "" return 2 end options[:json] = json end opt.on("-v","verbose") do |verbose| options[:verbose] = true end opt.on("-q","quiet") do |quiet| options[:quiet] = true end opt.on("-h","display help") do |help| options[:help] = true end opt.separator "" opt.separator "Return codes:" opt.separator " 0 = success" opt.separator " 1 = bad JCR parsing or other bad condition" opt.separator " 2 = invalid option or bad use of command" opt.separator " 3 = unsuccessful evaluation of JSON" opt.separator "" opt.separator "JCR Version " + JCR::VERSION end begin opt_parser.parse! my_argv rescue OptionParser::InvalidOption => e puts "Unable to interpret command or options" puts e.message puts "", "Use -h for help" return 2 end if options[:help] puts "HELP","----","" puts opt_parser return 2 elsif !options[:ruleset] puts "No ruleset passed! Use -R or -r options.", "" puts "Use -h for help" return 2 else begin ctx = Context.new( options[:ruleset], options[:verbose] ) if options[:overrides] options[:overrides].each do |ov| ctx.override!( ov ) end end if options[:verbose] pp "Ruleset Parse Tree", ctx.tree puts "Ruleset Map" ctx.mapping.each do |name,rule| puts "Parsed Rule: #{name}" puts rule_to_s( rule, false ) puts "Parsed Rule Structure: #{name}" pp rule end end if options[:process_parts] parts = JCR::JcrParts.new parts.process_ruleset( options[:ruleset], options[:process_parts_directory] ) if options[:overrides ] options[:overrides].each do |ov| parts = JCR::JcrParts.new parts.process_ruleset( ov, options[:process_parts_directory] ) end end end if options[:testjcr] #we got this far which means the JCR was already parsed without #issue. therefore return 0 return 0 elsif options[:json] data = JSON.parse( options[:json] ) ec = cli_eval( ctx, data, options[:root_name], options[:quiet] ) return ec elsif $stdin.tty? ec = 0 if my_argv.empty? ec = 2 else my_argv.each do |fn| data = JSON.parse( File.open( fn ).read ) tec = cli_eval( ctx, data, options[:root_name], options[:quiet] ) ec = tec if tec != 0 #record error but don't let non-error overwrite error end end return ec else lines = "" ec = 0 ARGF.each do |line| lines = lines + line if ARGF.eof? data = JSON.parse( lines ) tec = cli_eval( ctx, data, options[:root_name], options[:quiet] ) ec = tec if tec != 0 #record error but don't let non-error overwrite error lines = "" end end return ec end rescue JCR::JcrValidatorError => jcr_error puts jcr_error.message return 1 rescue Parslet::ParseFailed => failure puts failure.parse_failure_cause.ascii_tree unless options[:quiet] return 1 rescue JSON::ParserError => parser_error unless options[:quiet] puts "Unable to parse JSON" puts parser_error.message.inspect end return 3 end end end
# File lib/jcr/map_rule_names.rb, line 20 def self.map_rule_names( tree, ruleset_alias = nil ) prefix = "" if ruleset_alias prefix = ruleset_alias unless prefix.end_with?( "." ) prefix = prefix + "." end end rule_name_maping = Hash.new if tree.is_a? Hash tree = [ tree ] end tree.each do |node| if node[:rule] rn = prefix + node[:rule][:rule_name].to_str if rule_name_maping[ rn ] raise JCR::JcrValidatorError, "Rule #{rn} already exists and is defined more than once" else rule_name_maping[ rn ] = node[:rule] end end end return rule_name_maping end
# File lib/jcr/evaluate_member_rules.rb, line 82 def self.member_to_s( jcr, shallow=true ) rules, annotations = get_rules_and_annotations( jcr ) retval = "" rule = merge_rules( rules ) case when rule[:member_name] retval = %Q|"#{rule[:member_name][:q_string].to_s}"| when rule[:member_regex] retval = "/#{rule[:member_regex][:regex].to_s}/" else retval = "** unknown member rule **" end retval = retval + " : " + rule_to_s( rule, shallow ) return annotations_to_s( annotations ) + retval end
# File lib/jcr/evaluate_rules.rb, line 254 def self.merge_rules rules new_rule = Hash.new rules.each do |rule| new_rule.merge!(rule) do |key,oldval,newval| raise "error: conflict in merge of #{rule} with #{new_rule}" end end return new_rule end
# File lib/jcr/evaluate_object_rules.rb, line 192 def self.object_to_s( jcr, shallow=true ) rules, annotations = get_rules_and_annotations( jcr ) return "#{annotations_to_s( annotations)}{ #{rules_to_s(rules,shallow)} }" end
# File lib/jcr/parser.rb, line 462 def self.parse(str) parser = Parser.new parser.parse(str) end
# File lib/jcr/parser.rb, line 469 def self.parse_and_transform(str) # provided for the fun of it parser = Parser.new tree = parser.parse(str) pp tree transformer = Transformer.new transformer.apply( tree ) end
# File lib/jcr/evaluate_rules.rb, line 313 def self.pop_trace_stack econs econs.trace_stack.pop end
# File lib/jcr/parser.rb, line 481 def self.print_tree( tree ) tree.each do |node| puts "named rule: " + node[:rule][:rule_name] if node[:rule] end end
# File lib/jcr/process_directives.rb, line 25 def self.process_directives( ctx ) tree = ctx.tree if tree.is_a? Hash tree = [ tree ] end tree.each do |node| if node[:directive] d = node[:directive] case when d[:ruleset_id_d] process_ruleset_id( d[:ruleset_id_d], ctx ) when d[:import_d] process_import( d[:import_d], ctx ) when d[:jcr_version_d] process_jcrversion( d[:jcr_version_d], ctx ) end end end end
# File lib/jcr/process_directives.rb, line 62 def self.process_import( directive, ctx ) ruleset_id = directive[:ruleset_id].to_str ruleset_alias = directive[:ruleset_id_alias].to_str u = ctx.map_ruleset_alias( ruleset_alias, ruleset_id ) uri = URI.parse( u ) ruleset = nil case uri.scheme when "http","https" response = Net::HTTP.get_response uri ruleset = response.body else ruleset = File.open( uri.path ) end import_ctx = JCR.ingest_ruleset( ruleset, nil, ruleset_alias ) ctx.mapping.merge!( import_ctx.mapping ) ctx.roots.concat( import_ctx.roots ) end
# File lib/jcr/process_directives.rb, line 51 def self.process_jcrversion( directive, ctx ) major = directive[:major_version].to_str.to_i minor = directive[:minor_version].to_str.to_i if major != 0 raise "jcr version #{major}.#{minor} is incompatible with 0.7" end if minor != 7 raise "jcr version #{major}.#{minor} is incompatible with 0.7" end end
# File lib/jcr/process_directives.rb, line 47 def self.process_ruleset_id( directive, ctx ) ctx.id = directive[:ruleset_id].to_str end
# File lib/jcr/evaluate_rules.rb, line 309 def self.push_trace_stack econs, jcr econs.trace_stack.push( find_first_slice( jcr ) ) end
# File lib/jcr/check_groups.rb, line 177 def self.raise_group_error str, node if node.is_a?( Parslet::Slice ) pos = node.line_and_column name = node.to_str raise JCR::JcrValidatorError, "group rule error at line " + pos[0].to_s + " column " + pos[1].to_s + " name '" + name + "' :" + str else raise JCR::JcrValidatorError, "group rule error with '" + node.to_s + "' :" + str end end
# File lib/jcr/map_rule_names.rb, line 77 def self.raise_rule_name_missing rule_name pos = rule_name.line_and_column name = rule_name.to_str raise JCR::JcrValidatorError, "rule '" + name + "' at line " + pos[0].to_s + " column " + pos[1].to_s + " does not exist" end
# File lib/jcr/evaluate_rules.rb, line 446 def self.raised_rule jcr, rule_atom " rule at #{slice_to_s(jcr)} #{jcr_to_s(jcr)} from rule at #{slice_to_s(rule_atom)}" end
# File lib/jcr/evaluate_rules.rb, line 546 def self.repetitions_to_s rule retval = "" if rule[:optional] retval = "?" elsif rule[:one_or_more] retval = "+" if rule[:repetition_step] retval = "%" + rule[:repetition_step].to_s end elsif rule[:zero_or_more] retval = "*" retval = retval + "%" + rule[:repetition_step].to_s if rule[:repetition_step] elsif rule[:specific_repetition] && rule[:specific_repetition].is_a?( Parslet::Slice ) retval = "*" + rule[:specific_repetition].to_s else if rule[:repetition_interval] min = "0" max = "INF" o = rule[:repetition_min] if o if o.is_a?(Parslet::Slice) min = o.to_s end end o = rule[:repetition_max] if o if o.is_a?(Parslet::Slice) max = o.to_s end end retval = "*"+min+".."+max end o = rule[:repetition_step] if o if o.is_a?( Parslet::Slice ) retval = retval + "%" + o.to_s end end end retval = " " + retval if retval.length != 0 return retval end
# File lib/jcr/evaluate_rules.rb, line 325 def self.rule_data data=nil if data if data.is_a? String s = '"' + data + '"' else s = data.pretty_print_inspect end return elide(s) end #else return nil end
# File lib/jcr/evaluate_rules.rb, line 354 def self.rule_def type, jcr s = "" case type when "value" s = elide(value_to_s(jcr)) when "member" s = elide(member_to_s(jcr)) when "object" s = elide(object_to_s(jcr)) when "object group" s = elide(group_to_s(jcr)) when "array" s = elide(array_to_s(jcr)) when "array group" s = elide(array_to_s(jcr)) when "group" s = elide(group_to_s(jcr)) else s = "** unknown rule **" end return "#{type} definition << #{s} >>" end
# File lib/jcr/evaluate_rules.rb, line 471 def self.rule_to_s( rule, shallow=true) if rule[:rule_name] if rule[:primitive_rule] retval = "$#{rule[:rule_name].to_s} =: #{ruletype_to_s( rule, shallow )}" else retval = "$#{rule[:rule_name].to_s} = #{ruletype_to_s( rule, shallow )}" end else retval = ruletype_to_s( rule, shallow ) end return retval end
# File lib/jcr/evaluate_rules.rb, line 509 def self.rules_to_s( rules, shallow=true) retval = "" rules.each do |rule| if rule[:rule_name] next elsif rule[:choice_combiner] retval = retval + " | " elsif rule[:sequence_combiner] retval = retval + " , " end retval = retval + rule_to_s( rule, shallow ) + repetitions_to_s( rule ) end return retval end
# File lib/jcr/evaluate_rules.rb, line 484 def self.ruletype_to_s( rule, shallow=true ) if rule[:primitive_rule] retval = value_to_s( rule[:primitive_rule] ) elsif rule[:member_rule] retval = member_to_s( rule[:member_rule], shallow ) elsif rule[:object_rule] retval = object_to_s( rule[:object_rule], shallow ) elsif rule[:array_rule] retval = array_to_s( rule[:array_rule], shallow ) elsif rule[:group_rule] retval = group_to_s( rule[:group_rule], shallow ) elsif rule[:target_rule_name] retval = target_to_s( rule[:target_rule_name] ) elsif rule[:rule_name] retval = "rule: #{rule[:rule_name].to_s}" elsif rule[:rule] retval = rule_to_s( rule[:rule], shallow ) else retval = "** unknown rule definition ** #{rule}" end return retval end
# File lib/jcr/evaluate_rules.rb, line 435 def self.slice_to_s slice s = find_first_slice( slice ) if s.is_a? Parslet::Slice pos = s.line_and_column retval = "'#{s.inspect}' ( line #{pos[0]} column #{pos[1]} )" else retval = slice.to_s end retval end
# File lib/jcr/evaluate_rules.rb, line 542 def self.target_to_s( jcr ) return annotations_to_s( jcr[:annotations] ) + "$" + jcr[:rule_name].to_s end
# File lib/jcr/evaluate_rules.rb, line 338 def self.trace econs, message, data = nil if econs.trace if data message = "#{message} data: #{rule_data( data )}" end last = econs.trace_stack.last puts "[ depth=#{econs.trace_stack.length}:#{trace_coord(econs)} ] #{message}" end end
# File lib/jcr/evaluate_rules.rb, line 348 def self.trace_coord econs last = econs.trace_stack.last pos = "#{last.line_and_column}" if last return "pos=#{pos}" end
# File lib/jcr/evaluate_rules.rb, line 377 def self.trace_def econs, type, jcr, data if econs.trace s = "" case type when "value" s = elide( value_to_s( jcr ) ) when "member" s = elide( member_to_s( jcr ) ) when "object" s = elide( object_to_s( jcr ) ) when "object group" s = elide( group_to_s( jcr ) ) when "array" s = elide( array_to_s( jcr ) ) when "array group" s = elide( array_to_s( jcr ) ) when "group" s = elide( group_to_s( jcr ) ) else s = "** unknown rule **" end trace( econs, rule_def( type, jcr ) ) end end
# File lib/jcr/evaluate_rules.rb, line 402 def self.trace_eval econs, message, evaluation, jcr, data, type if evaluation.success econs.report_success trace( econs, "#{message} evaluation is true" ) else failure = Failure.new( data, jcr, type, evaluation, econs.trace_stack.length ) econs.report_failure( failure ) trace( econs, "#{message} evaluation failed: #{evaluation.reason}") end end
# File lib/jcr/evaluate_value_rules.rb, line 408 def self.value_to_s( jcr, shallow=true ) rules, annotations = get_rules_and_annotations( jcr ) rule = rules[ 0 ] retval = "" case when rule[:any] retval = "any" when rule[:integer_v] retval = rule[:integer_v].to_s when rule[:integer] retval = rule[:integer].to_s.to_i when rule[:integer_min],rule[:integer_max] min = "-INF" max = "INF" min = rule[:integer_min].to_s.to_i if rule[:integer_min] max = rule[:integer_max].to_s.to_i if rule[:integer_max] retval = "#{min}..#{max}" when rule[:sized_int_v] retval = "int" + rule[:sized_int_v][:bits].to_s when rule[:sized_uint_v] retval = "uint" + rule[:sized_uint_v][:bits].to_s when rule[:double_v] retval = rule[:double_v].to_s when rule[:float_v] retval = rule[:float_v].to_s when rule[:float] retval = rule[:float].to_s.to_f when rule[:float_min],rule[:float_max] min = "-INF" max = "INF" min = rule[:float_min].to_s.to_f if rule[:float_min] max = rule[:float_max].to_s.to_f if rule[:float_max] retval = "#{min}..#{max}" when rule[:true_v] retval = "true" when rule[:false_v] retval = "false" when rule[:boolean_v] retval = "boolean" when rule[:string] retval = "string" when rule[:q_string] retval = %Q|"#{rule[:q_string].to_s}"| when rule[:regex] retval = "/#{rule[:regex].to_s}/" when rule[:ipv4] retval = "ipv4" when rule[:ipv6] retval = "ipv6" when rule[:fqdn] retval = "fqdn" when rule[:idn] retval = "idn" when rule[:uri] if rule[:uri].is_a? Hash retval = "uri..#{rule[:uri][:uri_scheme].to_s}" else retval = "uri" end when rule[:email] retval = "email" when rule[:phone] retval = "phone" when rule[:hex] retval = "hex" when rule[:base32hex] retval = "base32hex" when rule[:base64url] retval = "base64url" when rule[:base64] retval = "base64" when rule[:datetime] retval = "datetime" when rule[:date] retval = "date" when rule[:time] retval = "time" when rule[:null] retval = "null" when rule[:group_rule] retval = group_to_s( rule[:group_rule], shallow ) else retval = "** unknown value rule **" end return annotations_to_s( annotations ) + retval.to_s end