Class: Diecut::Linter
- Inherits:
-
Object
- Object
- Diecut::Linter
- Defined in:
- lib/diecut/linter.rb
Instance Attribute Summary collapse
-
#mill ⇒ Object
readonly
Returns the value of attribute mill.
Instance Method Summary collapse
- #each_default ⇒ Object
- #each_option ⇒ Object
- #each_plugin ⇒ Object
-
#initialize(mill) ⇒ Linter
constructor
A new instance of Linter.
- #option_collision_report ⇒ Object
- #orphaned_fields ⇒ Object
- #overridden_context_defaults ⇒ Object
- #report ⇒ Object
-
#unindent(text) ⇒ Object
Needed: Overridden option defaults (without plugin dep) Option with default, context with default (w/o PD).
Constructor Details
#initialize(mill) ⇒ Linter
Returns a new instance of Linter.
5 6 7 |
# File 'lib/diecut/linter.rb', line 5 def initialize(mill) @mill = mill end |
Instance Attribute Details
#mill ⇒ Object (readonly)
Returns the value of attribute mill.
8 9 10 |
# File 'lib/diecut/linter.rb', line 8 def mill @mill end |
Instance Method Details
#each_default ⇒ Object
37 38 39 40 41 42 43 |
# File 'lib/diecut/linter.rb', line 37 def each_default each_plugin do |plugin| plugin.context_defaults.each do |default| yield default, plugin end end end |
#each_option ⇒ Object
45 46 47 48 49 50 51 |
# File 'lib/diecut/linter.rb', line 45 def each_option each_plugin do |plugin| plugin..each do |option| yield option, plugin end end end |
#each_plugin ⇒ Object
31 32 33 34 35 |
# File 'lib/diecut/linter.rb', line 31 def each_plugin mill.mediator.activated_plugins.each do |plugin| yield plugin end end |
#option_collision_report ⇒ Object
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/diecut/linter.rb', line 100 def option_collision_report Report.new("Option collisions", ["Output target", "Option name", "Source plugin"]).tap do |report| option_targets = Hash.new{|h,k| h[k]=[]} each_option do |option, plugin| next unless option.has_context_path? option_targets[option.context_path] << [plugin, option] end option_targets.each_value do |set| if set.length > 1 set.each do |plugin, option| report.add(option.context_path.join("."), option.name, plugin.name) end end end unless report.empty? report.fail("Multiple options assign the same values to be rendered") report.advice = unindent(<<-EOA) This is problem because two options in the user interface both change rendered values. If a user supplies both with different values, the output isn't predictable (either one might take effect). Most likely, this is a simple error: remove options from each group that targets the same rendered value until only one remains. It may also be that one option has a typo - that there's a rendering target that's omitted. EOA end end end |
#orphaned_fields ⇒ Object
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/diecut/linter.rb', line 131 def orphaned_fields Report.new("Template fields all have settings", ["Output field", "Source file"]).tap do |report| context_class = mill.context_class required_fields = {} context_class.field_names.each do |field_name| if context_class.(field_name).is?(:required) required_fields[field_name.to_s] = [] end end mill.templates.all_templates.each do |template| template.reduced.leaf_fields.each do |field| field = field.join(".") if required_fields.has_key?(field) required_fields[field] << template.path end end end each_option do |option, plugin| next unless option.has_context_path? field = option.context_path.join(".") required_fields.delete(field) end required_fields.each do |name, targets| targets.each do |target| report.add(name, target) end end unless report.empty? report.status = "WARN" report.advice = unindent(<<-EOA) These fields might not receive a value during generation, which will raise an error at use time. It's possible these fields are set in a resolve block in one of the plugins - Diecut can't check for that yet. EOA end end end |
#overridden_context_defaults ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/diecut/linter.rb', line 53 def overridden_context_defaults Report.new("Overridden context defaults", ["Output field", "Default value", "Source plugin"]).tap do |report| default_values = Hash.new{|h,k| h[k]=[]} each_default do |default, plugin| next unless default.simple? default_values[default.context_path] << [default, plugin] end default_values.each do |key, set| default_values[key] = set.find_all do |plugin| !set.any?{|child| next if child == plugin Diecut.plugin_loader.strict_sequence?(plugin[1], child[1]) } end end default_values.each_value do |set| if set.length > 1 set.each do |default, plugin| report.add(default.context_path.join("."), default.value, plugin.name) end end end unless report.empty? report.fail("Multiple plugins assign different values to be rendered") report.advice = unindent(<<-EOA) This is a problem because each plugin may be assuming it's default value, and since there's no guarantee in which order the plugins are loaded, the actual default value is difficult to predict. In general, this kind of override behavior can be difficult to reason about. Either the collision is accidental, in which case the default value should be removed from one plugin or the other. If the override is intentional, then the overriding plugin's gem should depend on the overridden one's - since you are overriding the value intentionally, it makes sense to ensure that the value is there to override. Diecut will load plugins such that the dependant plugins are loaded later, which solves the predictability problem. EOA end end end |
#report ⇒ Object
10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/diecut/linter.rb', line 10 def report @ui = mill.user_interface formatter = ReportFormatter.new([ option_collision_report, orphaned_fields, overridden_context_defaults ]) formatter.to_s end |
#unindent(text) ⇒ Object
Needed: Overridden option defaults (without plugin dep) Option with default, context with default (w/o PD)
26 27 28 29 |
# File 'lib/diecut/linter.rb', line 26 def unindent(text) indent = text.scan(/(^[ \t]*)\S/).map{|cap| cap.first}.max_by(&:length) text.gsub(%r{^#{indent}},'') end |