Class: Synthra::CLI::Commands::Diff
- Defined in:
- lib/synthra/cli/commands/diff.rb
Overview
Diff command - compares schema versions
Instance Method Summary collapse
- #call ⇒ Object
- #compare_schemas(s1, s2) ⇒ Object private
- #compute_breaking_changes(schemas1, schemas2, all_names) ⇒ Object private
-
#compute_diff(registry1, registry2) ⇒ Hash
private
Compute detailed diff between two registries.
- #compute_modified(schemas1, schemas2, all_names) ⇒ Object private
- #display_breaking_changes(changes, path1, path2) ⇒ Object private
- #display_full_diff(changes, path1, path2) ⇒ Object private
Constructor Details
This class inherits a constructor from Synthra::CLI::Commands::Base
Instance Method Details
#call ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/synthra/cli/commands/diff.rb', line 13 def call path1 = args.shift path2 = args.shift unless path1 && path2 error "Usage: synthra diff <path1> <path2> [--breaking-only]" return EXIT_PARSE_ERROR end registry1 = Registry.new registry2 = Registry.new load_path(registry1, path1) load_path(registry2, path2) changes = compute_diff(registry1, registry2) if [:breaking_only] display_breaking_changes(changes, path1, path2) else display_full_diff(changes, path1, path2) end end |
#compare_schemas(s1, s2) ⇒ Object (private)
128 129 130 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 |
# File 'lib/synthra/cli/commands/diff.rb', line 128 def compare_schemas(s1, s2) diff = [] # Compare versions if s1.version != s2.version diff << "version: #{s1.version || 'nil'} → #{s2.version || 'nil'}" end # Compare deprecation if s1.deprecated? != s2.deprecated? diff << "deprecated: #{s1.deprecated?} → #{s2.deprecated?}" end # Compare fields f1_names = s1.fields.map(&:name) f2_names = s2.fields.map(&:name) (f2_names - f1_names).each { |f| diff << "+ field: #{f}" } (f1_names - f2_names).each { |f| diff << "- field: #{f}" } # Compare field types (f1_names & f2_names).each do |name| field1 = s1.field(name) field2 = s2.field(name) if field1.type_name != field2.type_name diff << "~ #{name}: #{field1.type_name} → #{field2.type_name}" end end diff end |
#compute_breaking_changes(schemas1, schemas2, all_names) ⇒ Object (private)
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 99 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 |
# File 'lib/synthra/cli/commands/diff.rb', line 69 def compute_breaking_changes(schemas1, schemas2, all_names) breaking = [] # Removed schemas are breaking (schemas1.keys - schemas2.keys).each do |name| breaking << { schema: name, type: :schema_removed, message: "Schema '#{name}' removed" } end # Check field changes (schemas1.keys & schemas2.keys).each do |name| s1 = schemas1[name] s2 = schemas2[name] # Removed fields are breaking f1_names = s1.fields.map(&:name) f2_names = s2.fields.map(&:name) (f1_names - f2_names).each do |field_name| breaking << { schema: name, type: :field_removed, field: field_name, message: "Field '#{field_name}' removed from '#{name}'" } end # Type changes are breaking (f1_names & f2_names).each do |field_name| field1 = s1.field(field_name) field2 = s2.field(field_name) if field1.type_name != field2.type_name breaking << { schema: name, type: :type_changed, field: field_name, from: field1.type_name, to: field2.type_name, message: "Field '#{field_name}' type changed: #{field1.type_name} → #{field2.type_name}" } end # Required → Optional is not breaking, but Optional → Required is if !field1.optional? && field2.optional? # Not breaking elsif field1.optional? && !field2.optional? breaking << { schema: name, type: :required_added, field: field_name, message: "Field '#{field_name}' changed from optional to required" } end end end breaking end |
#compute_diff(registry1, registry2) ⇒ Hash (private)
Compute detailed diff between two registries
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/synthra/cli/commands/diff.rb', line 43 def compute_diff(registry1, registry2) schemas1 = registry1.schemas schemas2 = registry2.schemas all_names = (schemas1.keys + schemas2.keys).uniq.sort { added: schemas2.keys - schemas1.keys, removed: schemas1.keys - schemas2.keys, modified: compute_modified(schemas1, schemas2, all_names), breaking: compute_breaking_changes(schemas1, schemas2, all_names) } end |
#compute_modified(schemas1, schemas2, all_names) ⇒ Object (private)
56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/synthra/cli/commands/diff.rb', line 56 def compute_modified(schemas1, schemas2, all_names) modified = {} (schemas1.keys & schemas2.keys).each do |name| s1 = schemas1[name] s2 = schemas2[name] changes = compare_schemas(s1, s2) modified[name] = changes if changes.any? end modified end |
#display_breaking_changes(changes, path1, path2) ⇒ Object (private)
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/synthra/cli/commands/diff.rb', line 160 def display_breaking_changes(changes, path1, path2) breaking = changes[:breaking] if breaking.empty? success "No breaking changes detected" return EXIT_SUCCESS end puts "=" * 60 puts "BREAKING CHANGES DETECTED" puts "=" * 60 puts puts "Comparing: #{path1} → #{path2}" puts breaking.each do |change| puts " ✗ #{change[:message]}" end puts error "#{breaking.length} breaking change(s) found" EXIT_PARSE_ERROR end |
#display_full_diff(changes, path1, path2) ⇒ Object (private)
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/synthra/cli/commands/diff.rb', line 184 def display_full_diff(changes, path1, path2) puts "=" * 60 puts "Schema Diff" puts "=" * 60 puts "Comparing: #{path1} → #{path2}" puts has_diff = false # Added schemas changes[:added].each do |name| puts "➕ #{name} (added)" has_diff = true end # Removed schemas changes[:removed].each do |name| puts "➖ #{name} (removed)" has_diff = true end # Modified schemas changes[:modified].each do |name, diffs| puts "📝 #{name} (modified)" diffs.each { |d| puts " #{d}" } has_diff = true end unless has_diff success "No differences found" end # Show breaking changes summary if any if changes[:breaking].any? puts puts "⚠️ Breaking changes: #{changes[:breaking].length}" end EXIT_SUCCESS end |