Class: Bundler::GemBytes::Actions::Gemspec::UpsertDependency
- Inherits:
-
Object
- Object
- Bundler::GemBytes::Actions::Gemspec::UpsertDependency
- Defined in:
- lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb
Overview
Add or update a dependency in a gemspec
If a dependency on the given gem is not found, a new dependency is added to the end of the Gem::Specification block.
If one or more dependencies are found on the same gem as new_dependency, the version constraint is updated to the new_dependency version constraint.
The gemspec is updated via calls to the tree_rewriter object.
Instance Attribute Summary collapse
-
#dependencies ⇒ Array<DependencyNode>
readonly
private
The dependency declarations found in the gemspec file.
-
#gemspec_block ⇒ Parser::AST::Node
readonly
private
The root AST node of the Gem::Specification block from the gemspec.
-
#new_dependency ⇒ Dependency
readonly
private
The dependency declaration to add or update.
-
#receiver_name ⇒ Symbol
readonly
private
The name of the receiver for the Gem::Specification block.
-
#tree_rewriter ⇒ Parser::TreeRewriter
readonly
private
The object that updates the source.
Instance Method Summary collapse
-
#add_dependency ⇒ void
private
Add the new_dependency to the end of the Gem::Specification block.
-
#add_dependency_to_empty_gemspec_block ⇒ void
private
Add new_dependency to an empty Gem::Specification block.
-
#call(new_dependency) ⇒ void
Adds or updates a dependency to the Gem::Specification block.
-
#dependency_method_to_type(method) ⇒ Symbol
private
The dependency type (:runtime or :development) based on a given method name.
-
#dependency_source_code(existing_dependency = nil) ⇒ String
private
The source code for the updated dependency declaration.
-
#dependency_type_conflict_error(existing_dependency) ⇒ String
private
Error message for a dependency type conflict.
-
#dependency_type_match?(existing_dependency) ⇒ Boolean
private
Checks if the new dependency type is the same as the existing dependency type.
-
#initialize(tree_rewriter, gemspec_block, receiver_name, dependencies) ⇒ UpsertDependency
constructor
private
Initializes the upsert dependency action.
-
#new_method_name(existing_dependency) ⇒ Symbol
private
The method to use for the new dependency.
-
#new_quote_char(existing_dependency) ⇒ String
private
Use the same quote char as the existing dependency or default to single quote.
-
#update_dependencies(matching_dependencies) ⇒ void
private
Update the version constraint of the existing dependency(s).
Constructor Details
#initialize(tree_rewriter, gemspec_block, receiver_name, dependencies) ⇒ UpsertDependency
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Initializes the upsert dependency action
54 55 56 57 58 59 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 54 def initialize(tree_rewriter, gemspec_block, receiver_name, dependencies) @tree_rewriter = tree_rewriter @gemspec_block = gemspec_block @receiver_name = receiver_name @dependencies = dependencies end |
Instance Attribute Details
#dependencies ⇒ Array<DependencyNode> (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The dependency declarations found in the gemspec file
47 48 49 50 51 52 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 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 127 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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 47 class UpsertDependency # Initializes the upsert dependency action # @param tree_rewriter [Parser::TreeRewriter] The object that updates the source # @param gemspec_block [Parser::AST::Node] The Gem::Specification block # @param receiver_name [Symbol] The name of the receiver for the Gem::Specification block # @param dependencies [Array<DependencyNode>] The dependency declarations found in the gemspec file # @api private def initialize(tree_rewriter, gemspec_block, receiver_name, dependencies) @tree_rewriter = tree_rewriter @gemspec_block = gemspec_block @receiver_name = receiver_name @dependencies = dependencies end attr_reader :tree_rewriter, :gemspec_block, :receiver_name, :dependencies, :new_dependency # Adds or updates a dependency to the Gem::Specification block # # @example # upsert_dependency = UpsertDependency.new(tree_rewriter, gemspec_block, receiver_name, dependencies) # new_dependency = Dependency.new(:add_runtime_dependency, 'rubocop', '~> 1.68') # upsert_dependency.call(new_dependency) # @param new_dependency [Dependency] The dependency declaration to add or update # @return [void] # @api public def call(new_dependency) @new_dependency = new_dependency matching_dependencies = dependencies.select { |d| d.dependency.gem_name == new_dependency.gem_name } if matching_dependencies.any? update_dependencies(matching_dependencies) else add_dependency end end # Update the version constraint of the existing dependency(s) # @param matching_dependencies [Array<DependencyNode>] The existing dependencies that match new_dependency # @return [void] # @api private def update_dependencies(matching_dependencies) matching_dependencies.each do |found_dependency| raise(dependency_type_conflict_error(found_dependency)) unless dependency_type_match?(found_dependency) tree_rewriter.replace(found_dependency.node.loc.expression, dependency_source_code(found_dependency)) end end # Add the new_dependency to the end of the Gem::Specification block # @return [void] # @api private def add_dependency # Add the dependency to the end of the Gem::Specification block internal_block = gemspec_block.children[2] if internal_block tree_rewriter.insert_after(internal_block.children.last.loc.expression, "\n #{dependency_source_code}") else # When the Gem::Specification block is empty, it require special handling add_dependency_to_empty_gemspec_block end end # Error message for a dependency type conflict # @param existing_dependency [DependencyNode] The existing dependency # @return [String] The error message # @api private def dependency_type_conflict_error(existing_dependency) # :nocov: JRuby give false positive for this line being uncovered by tests <<~MESSAGE.chomp.gsub("\n", ' ') Trying to add a #{dependency_method_to_type(new_dependency.method_name).upcase} dependency on "#{new_dependency.gem_name}" which conflicts with the existing #{dependency_method_to_type(existing_dependency.dependency.method_name).upcase} dependency. MESSAGE # :nocov: end # The dependency type (:runtime or :development) based on a given method name # @param method [Symbol] The method name to convert to a dependency type # @return [Symbol] The dependency type # @api private def dependency_method_to_type(method) method == :add_development_dependency ? :development : :runtime end # Checks if the new dependency type is the same as the existing dependency type # # @param existing_dependency [DependencyNode] The existing dependency # @return [Boolean] Whether the dependency type conflicts # @api private def dependency_type_match?(existing_dependency) # Either both are :add_development_dependency or both are not (existing_dependency.dependency.method_name == :add_development_dependency) == (new_dependency.method_name == :add_development_dependency) end # Add new_dependency to an empty Gem::Specification block # @return [void] # @api private def add_dependency_to_empty_gemspec_block source = gemspec_block.loc.expression.source # :nocov: supress false reporting of no coverage of multiline string literals on JRuby tree_rewriter.replace(gemspec_block.loc.expression, <<~GEMSPEC_BLOCK.chomp) #{source[0..-5]} #{dependency_source_code} #{source[-3..]} GEMSPEC_BLOCK # :nocov: end # The source code for the updated dependency declaration # @param existing_dependency [DependencyNode] The existing dependency # @return [String] The source code for the dependency declaration # @api private def dependency_source_code(existing_dependency = nil) # Use existing quote character for string literals q = new_quote_char(existing_dependency) # :nocov: supress false reporting of no coverage of multiline string literals on JRuby "#{receiver_name}.#{new_method_name(existing_dependency)} " \ "#{q}#{new_dependency.gem_name}#{q}, " \ "#{new_dependency.version_constraints.map { |vc| "#{q}#{vc}#{q}" }.join(', ')}" # :nocov: end # Use the same quote char as the existing dependency or default to single quote # @param existing_dependency [DependencyNode, nil] The existing dependency being updated # @return [String] The quote character to use # @api private def new_quote_char(existing_dependency) if existing_dependency existing_dependency.node.children[3].loc.expression.source[0] else "'" end end # The method to use for the new dependency # # If `existing_dependency` is given and the dependency type (runtime vs. # development) matches, the existing dependency method is used. Otherwise, # the new_dependency method is used. # # The purpose of this method is ensure that an #add_dependency call is not # replaced with an #add_runtime_dependency call or vice versa. This # maintains consistency within the user's gemspec even though these methods # are functionally equivalent. # # @param existing_dependency [DependencyNode, nil] The existing dependency being updated # @return [Symbol] The method to use for the new dependency # @api private def new_method_name(existing_dependency) if existing_dependency && dependency_type_match?(existing_dependency) existing_dependency.dependency.method_name else new_dependency.method_name end end end |
#gemspec_block ⇒ Parser::AST::Node (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The root AST node of the Gem::Specification block from the gemspec
47 48 49 50 51 52 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 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 127 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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 47 class UpsertDependency # Initializes the upsert dependency action # @param tree_rewriter [Parser::TreeRewriter] The object that updates the source # @param gemspec_block [Parser::AST::Node] The Gem::Specification block # @param receiver_name [Symbol] The name of the receiver for the Gem::Specification block # @param dependencies [Array<DependencyNode>] The dependency declarations found in the gemspec file # @api private def initialize(tree_rewriter, gemspec_block, receiver_name, dependencies) @tree_rewriter = tree_rewriter @gemspec_block = gemspec_block @receiver_name = receiver_name @dependencies = dependencies end attr_reader :tree_rewriter, :gemspec_block, :receiver_name, :dependencies, :new_dependency # Adds or updates a dependency to the Gem::Specification block # # @example # upsert_dependency = UpsertDependency.new(tree_rewriter, gemspec_block, receiver_name, dependencies) # new_dependency = Dependency.new(:add_runtime_dependency, 'rubocop', '~> 1.68') # upsert_dependency.call(new_dependency) # @param new_dependency [Dependency] The dependency declaration to add or update # @return [void] # @api public def call(new_dependency) @new_dependency = new_dependency matching_dependencies = dependencies.select { |d| d.dependency.gem_name == new_dependency.gem_name } if matching_dependencies.any? update_dependencies(matching_dependencies) else add_dependency end end # Update the version constraint of the existing dependency(s) # @param matching_dependencies [Array<DependencyNode>] The existing dependencies that match new_dependency # @return [void] # @api private def update_dependencies(matching_dependencies) matching_dependencies.each do |found_dependency| raise(dependency_type_conflict_error(found_dependency)) unless dependency_type_match?(found_dependency) tree_rewriter.replace(found_dependency.node.loc.expression, dependency_source_code(found_dependency)) end end # Add the new_dependency to the end of the Gem::Specification block # @return [void] # @api private def add_dependency # Add the dependency to the end of the Gem::Specification block internal_block = gemspec_block.children[2] if internal_block tree_rewriter.insert_after(internal_block.children.last.loc.expression, "\n #{dependency_source_code}") else # When the Gem::Specification block is empty, it require special handling add_dependency_to_empty_gemspec_block end end # Error message for a dependency type conflict # @param existing_dependency [DependencyNode] The existing dependency # @return [String] The error message # @api private def dependency_type_conflict_error(existing_dependency) # :nocov: JRuby give false positive for this line being uncovered by tests <<~MESSAGE.chomp.gsub("\n", ' ') Trying to add a #{dependency_method_to_type(new_dependency.method_name).upcase} dependency on "#{new_dependency.gem_name}" which conflicts with the existing #{dependency_method_to_type(existing_dependency.dependency.method_name).upcase} dependency. MESSAGE # :nocov: end # The dependency type (:runtime or :development) based on a given method name # @param method [Symbol] The method name to convert to a dependency type # @return [Symbol] The dependency type # @api private def dependency_method_to_type(method) method == :add_development_dependency ? :development : :runtime end # Checks if the new dependency type is the same as the existing dependency type # # @param existing_dependency [DependencyNode] The existing dependency # @return [Boolean] Whether the dependency type conflicts # @api private def dependency_type_match?(existing_dependency) # Either both are :add_development_dependency or both are not (existing_dependency.dependency.method_name == :add_development_dependency) == (new_dependency.method_name == :add_development_dependency) end # Add new_dependency to an empty Gem::Specification block # @return [void] # @api private def add_dependency_to_empty_gemspec_block source = gemspec_block.loc.expression.source # :nocov: supress false reporting of no coverage of multiline string literals on JRuby tree_rewriter.replace(gemspec_block.loc.expression, <<~GEMSPEC_BLOCK.chomp) #{source[0..-5]} #{dependency_source_code} #{source[-3..]} GEMSPEC_BLOCK # :nocov: end # The source code for the updated dependency declaration # @param existing_dependency [DependencyNode] The existing dependency # @return [String] The source code for the dependency declaration # @api private def dependency_source_code(existing_dependency = nil) # Use existing quote character for string literals q = new_quote_char(existing_dependency) # :nocov: supress false reporting of no coverage of multiline string literals on JRuby "#{receiver_name}.#{new_method_name(existing_dependency)} " \ "#{q}#{new_dependency.gem_name}#{q}, " \ "#{new_dependency.version_constraints.map { |vc| "#{q}#{vc}#{q}" }.join(', ')}" # :nocov: end # Use the same quote char as the existing dependency or default to single quote # @param existing_dependency [DependencyNode, nil] The existing dependency being updated # @return [String] The quote character to use # @api private def new_quote_char(existing_dependency) if existing_dependency existing_dependency.node.children[3].loc.expression.source[0] else "'" end end # The method to use for the new dependency # # If `existing_dependency` is given and the dependency type (runtime vs. # development) matches, the existing dependency method is used. Otherwise, # the new_dependency method is used. # # The purpose of this method is ensure that an #add_dependency call is not # replaced with an #add_runtime_dependency call or vice versa. This # maintains consistency within the user's gemspec even though these methods # are functionally equivalent. # # @param existing_dependency [DependencyNode, nil] The existing dependency being updated # @return [Symbol] The method to use for the new dependency # @api private def new_method_name(existing_dependency) if existing_dependency && dependency_type_match?(existing_dependency) existing_dependency.dependency.method_name else new_dependency.method_name end end end |
#new_dependency ⇒ Dependency (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The dependency declaration to add or update
47 48 49 50 51 52 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 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 127 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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 47 class UpsertDependency # Initializes the upsert dependency action # @param tree_rewriter [Parser::TreeRewriter] The object that updates the source # @param gemspec_block [Parser::AST::Node] The Gem::Specification block # @param receiver_name [Symbol] The name of the receiver for the Gem::Specification block # @param dependencies [Array<DependencyNode>] The dependency declarations found in the gemspec file # @api private def initialize(tree_rewriter, gemspec_block, receiver_name, dependencies) @tree_rewriter = tree_rewriter @gemspec_block = gemspec_block @receiver_name = receiver_name @dependencies = dependencies end attr_reader :tree_rewriter, :gemspec_block, :receiver_name, :dependencies, :new_dependency # Adds or updates a dependency to the Gem::Specification block # # @example # upsert_dependency = UpsertDependency.new(tree_rewriter, gemspec_block, receiver_name, dependencies) # new_dependency = Dependency.new(:add_runtime_dependency, 'rubocop', '~> 1.68') # upsert_dependency.call(new_dependency) # @param new_dependency [Dependency] The dependency declaration to add or update # @return [void] # @api public def call(new_dependency) @new_dependency = new_dependency matching_dependencies = dependencies.select { |d| d.dependency.gem_name == new_dependency.gem_name } if matching_dependencies.any? update_dependencies(matching_dependencies) else add_dependency end end # Update the version constraint of the existing dependency(s) # @param matching_dependencies [Array<DependencyNode>] The existing dependencies that match new_dependency # @return [void] # @api private def update_dependencies(matching_dependencies) matching_dependencies.each do |found_dependency| raise(dependency_type_conflict_error(found_dependency)) unless dependency_type_match?(found_dependency) tree_rewriter.replace(found_dependency.node.loc.expression, dependency_source_code(found_dependency)) end end # Add the new_dependency to the end of the Gem::Specification block # @return [void] # @api private def add_dependency # Add the dependency to the end of the Gem::Specification block internal_block = gemspec_block.children[2] if internal_block tree_rewriter.insert_after(internal_block.children.last.loc.expression, "\n #{dependency_source_code}") else # When the Gem::Specification block is empty, it require special handling add_dependency_to_empty_gemspec_block end end # Error message for a dependency type conflict # @param existing_dependency [DependencyNode] The existing dependency # @return [String] The error message # @api private def dependency_type_conflict_error(existing_dependency) # :nocov: JRuby give false positive for this line being uncovered by tests <<~MESSAGE.chomp.gsub("\n", ' ') Trying to add a #{dependency_method_to_type(new_dependency.method_name).upcase} dependency on "#{new_dependency.gem_name}" which conflicts with the existing #{dependency_method_to_type(existing_dependency.dependency.method_name).upcase} dependency. MESSAGE # :nocov: end # The dependency type (:runtime or :development) based on a given method name # @param method [Symbol] The method name to convert to a dependency type # @return [Symbol] The dependency type # @api private def dependency_method_to_type(method) method == :add_development_dependency ? :development : :runtime end # Checks if the new dependency type is the same as the existing dependency type # # @param existing_dependency [DependencyNode] The existing dependency # @return [Boolean] Whether the dependency type conflicts # @api private def dependency_type_match?(existing_dependency) # Either both are :add_development_dependency or both are not (existing_dependency.dependency.method_name == :add_development_dependency) == (new_dependency.method_name == :add_development_dependency) end # Add new_dependency to an empty Gem::Specification block # @return [void] # @api private def add_dependency_to_empty_gemspec_block source = gemspec_block.loc.expression.source # :nocov: supress false reporting of no coverage of multiline string literals on JRuby tree_rewriter.replace(gemspec_block.loc.expression, <<~GEMSPEC_BLOCK.chomp) #{source[0..-5]} #{dependency_source_code} #{source[-3..]} GEMSPEC_BLOCK # :nocov: end # The source code for the updated dependency declaration # @param existing_dependency [DependencyNode] The existing dependency # @return [String] The source code for the dependency declaration # @api private def dependency_source_code(existing_dependency = nil) # Use existing quote character for string literals q = new_quote_char(existing_dependency) # :nocov: supress false reporting of no coverage of multiline string literals on JRuby "#{receiver_name}.#{new_method_name(existing_dependency)} " \ "#{q}#{new_dependency.gem_name}#{q}, " \ "#{new_dependency.version_constraints.map { |vc| "#{q}#{vc}#{q}" }.join(', ')}" # :nocov: end # Use the same quote char as the existing dependency or default to single quote # @param existing_dependency [DependencyNode, nil] The existing dependency being updated # @return [String] The quote character to use # @api private def new_quote_char(existing_dependency) if existing_dependency existing_dependency.node.children[3].loc.expression.source[0] else "'" end end # The method to use for the new dependency # # If `existing_dependency` is given and the dependency type (runtime vs. # development) matches, the existing dependency method is used. Otherwise, # the new_dependency method is used. # # The purpose of this method is ensure that an #add_dependency call is not # replaced with an #add_runtime_dependency call or vice versa. This # maintains consistency within the user's gemspec even though these methods # are functionally equivalent. # # @param existing_dependency [DependencyNode, nil] The existing dependency being updated # @return [Symbol] The method to use for the new dependency # @api private def new_method_name(existing_dependency) if existing_dependency && dependency_type_match?(existing_dependency) existing_dependency.dependency.method_name else new_dependency.method_name end end end |
#receiver_name ⇒ Symbol (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The name of the receiver for the Gem::Specification block
47 48 49 50 51 52 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 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 127 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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 47 class UpsertDependency # Initializes the upsert dependency action # @param tree_rewriter [Parser::TreeRewriter] The object that updates the source # @param gemspec_block [Parser::AST::Node] The Gem::Specification block # @param receiver_name [Symbol] The name of the receiver for the Gem::Specification block # @param dependencies [Array<DependencyNode>] The dependency declarations found in the gemspec file # @api private def initialize(tree_rewriter, gemspec_block, receiver_name, dependencies) @tree_rewriter = tree_rewriter @gemspec_block = gemspec_block @receiver_name = receiver_name @dependencies = dependencies end attr_reader :tree_rewriter, :gemspec_block, :receiver_name, :dependencies, :new_dependency # Adds or updates a dependency to the Gem::Specification block # # @example # upsert_dependency = UpsertDependency.new(tree_rewriter, gemspec_block, receiver_name, dependencies) # new_dependency = Dependency.new(:add_runtime_dependency, 'rubocop', '~> 1.68') # upsert_dependency.call(new_dependency) # @param new_dependency [Dependency] The dependency declaration to add or update # @return [void] # @api public def call(new_dependency) @new_dependency = new_dependency matching_dependencies = dependencies.select { |d| d.dependency.gem_name == new_dependency.gem_name } if matching_dependencies.any? update_dependencies(matching_dependencies) else add_dependency end end # Update the version constraint of the existing dependency(s) # @param matching_dependencies [Array<DependencyNode>] The existing dependencies that match new_dependency # @return [void] # @api private def update_dependencies(matching_dependencies) matching_dependencies.each do |found_dependency| raise(dependency_type_conflict_error(found_dependency)) unless dependency_type_match?(found_dependency) tree_rewriter.replace(found_dependency.node.loc.expression, dependency_source_code(found_dependency)) end end # Add the new_dependency to the end of the Gem::Specification block # @return [void] # @api private def add_dependency # Add the dependency to the end of the Gem::Specification block internal_block = gemspec_block.children[2] if internal_block tree_rewriter.insert_after(internal_block.children.last.loc.expression, "\n #{dependency_source_code}") else # When the Gem::Specification block is empty, it require special handling add_dependency_to_empty_gemspec_block end end # Error message for a dependency type conflict # @param existing_dependency [DependencyNode] The existing dependency # @return [String] The error message # @api private def dependency_type_conflict_error(existing_dependency) # :nocov: JRuby give false positive for this line being uncovered by tests <<~MESSAGE.chomp.gsub("\n", ' ') Trying to add a #{dependency_method_to_type(new_dependency.method_name).upcase} dependency on "#{new_dependency.gem_name}" which conflicts with the existing #{dependency_method_to_type(existing_dependency.dependency.method_name).upcase} dependency. MESSAGE # :nocov: end # The dependency type (:runtime or :development) based on a given method name # @param method [Symbol] The method name to convert to a dependency type # @return [Symbol] The dependency type # @api private def dependency_method_to_type(method) method == :add_development_dependency ? :development : :runtime end # Checks if the new dependency type is the same as the existing dependency type # # @param existing_dependency [DependencyNode] The existing dependency # @return [Boolean] Whether the dependency type conflicts # @api private def dependency_type_match?(existing_dependency) # Either both are :add_development_dependency or both are not (existing_dependency.dependency.method_name == :add_development_dependency) == (new_dependency.method_name == :add_development_dependency) end # Add new_dependency to an empty Gem::Specification block # @return [void] # @api private def add_dependency_to_empty_gemspec_block source = gemspec_block.loc.expression.source # :nocov: supress false reporting of no coverage of multiline string literals on JRuby tree_rewriter.replace(gemspec_block.loc.expression, <<~GEMSPEC_BLOCK.chomp) #{source[0..-5]} #{dependency_source_code} #{source[-3..]} GEMSPEC_BLOCK # :nocov: end # The source code for the updated dependency declaration # @param existing_dependency [DependencyNode] The existing dependency # @return [String] The source code for the dependency declaration # @api private def dependency_source_code(existing_dependency = nil) # Use existing quote character for string literals q = new_quote_char(existing_dependency) # :nocov: supress false reporting of no coverage of multiline string literals on JRuby "#{receiver_name}.#{new_method_name(existing_dependency)} " \ "#{q}#{new_dependency.gem_name}#{q}, " \ "#{new_dependency.version_constraints.map { |vc| "#{q}#{vc}#{q}" }.join(', ')}" # :nocov: end # Use the same quote char as the existing dependency or default to single quote # @param existing_dependency [DependencyNode, nil] The existing dependency being updated # @return [String] The quote character to use # @api private def new_quote_char(existing_dependency) if existing_dependency existing_dependency.node.children[3].loc.expression.source[0] else "'" end end # The method to use for the new dependency # # If `existing_dependency` is given and the dependency type (runtime vs. # development) matches, the existing dependency method is used. Otherwise, # the new_dependency method is used. # # The purpose of this method is ensure that an #add_dependency call is not # replaced with an #add_runtime_dependency call or vice versa. This # maintains consistency within the user's gemspec even though these methods # are functionally equivalent. # # @param existing_dependency [DependencyNode, nil] The existing dependency being updated # @return [Symbol] The method to use for the new dependency # @api private def new_method_name(existing_dependency) if existing_dependency && dependency_type_match?(existing_dependency) existing_dependency.dependency.method_name else new_dependency.method_name end end end |
#tree_rewriter ⇒ Parser::TreeRewriter (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The object that updates the source
47 48 49 50 51 52 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 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 127 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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 47 class UpsertDependency # Initializes the upsert dependency action # @param tree_rewriter [Parser::TreeRewriter] The object that updates the source # @param gemspec_block [Parser::AST::Node] The Gem::Specification block # @param receiver_name [Symbol] The name of the receiver for the Gem::Specification block # @param dependencies [Array<DependencyNode>] The dependency declarations found in the gemspec file # @api private def initialize(tree_rewriter, gemspec_block, receiver_name, dependencies) @tree_rewriter = tree_rewriter @gemspec_block = gemspec_block @receiver_name = receiver_name @dependencies = dependencies end attr_reader :tree_rewriter, :gemspec_block, :receiver_name, :dependencies, :new_dependency # Adds or updates a dependency to the Gem::Specification block # # @example # upsert_dependency = UpsertDependency.new(tree_rewriter, gemspec_block, receiver_name, dependencies) # new_dependency = Dependency.new(:add_runtime_dependency, 'rubocop', '~> 1.68') # upsert_dependency.call(new_dependency) # @param new_dependency [Dependency] The dependency declaration to add or update # @return [void] # @api public def call(new_dependency) @new_dependency = new_dependency matching_dependencies = dependencies.select { |d| d.dependency.gem_name == new_dependency.gem_name } if matching_dependencies.any? update_dependencies(matching_dependencies) else add_dependency end end # Update the version constraint of the existing dependency(s) # @param matching_dependencies [Array<DependencyNode>] The existing dependencies that match new_dependency # @return [void] # @api private def update_dependencies(matching_dependencies) matching_dependencies.each do |found_dependency| raise(dependency_type_conflict_error(found_dependency)) unless dependency_type_match?(found_dependency) tree_rewriter.replace(found_dependency.node.loc.expression, dependency_source_code(found_dependency)) end end # Add the new_dependency to the end of the Gem::Specification block # @return [void] # @api private def add_dependency # Add the dependency to the end of the Gem::Specification block internal_block = gemspec_block.children[2] if internal_block tree_rewriter.insert_after(internal_block.children.last.loc.expression, "\n #{dependency_source_code}") else # When the Gem::Specification block is empty, it require special handling add_dependency_to_empty_gemspec_block end end # Error message for a dependency type conflict # @param existing_dependency [DependencyNode] The existing dependency # @return [String] The error message # @api private def dependency_type_conflict_error(existing_dependency) # :nocov: JRuby give false positive for this line being uncovered by tests <<~MESSAGE.chomp.gsub("\n", ' ') Trying to add a #{dependency_method_to_type(new_dependency.method_name).upcase} dependency on "#{new_dependency.gem_name}" which conflicts with the existing #{dependency_method_to_type(existing_dependency.dependency.method_name).upcase} dependency. MESSAGE # :nocov: end # The dependency type (:runtime or :development) based on a given method name # @param method [Symbol] The method name to convert to a dependency type # @return [Symbol] The dependency type # @api private def dependency_method_to_type(method) method == :add_development_dependency ? :development : :runtime end # Checks if the new dependency type is the same as the existing dependency type # # @param existing_dependency [DependencyNode] The existing dependency # @return [Boolean] Whether the dependency type conflicts # @api private def dependency_type_match?(existing_dependency) # Either both are :add_development_dependency or both are not (existing_dependency.dependency.method_name == :add_development_dependency) == (new_dependency.method_name == :add_development_dependency) end # Add new_dependency to an empty Gem::Specification block # @return [void] # @api private def add_dependency_to_empty_gemspec_block source = gemspec_block.loc.expression.source # :nocov: supress false reporting of no coverage of multiline string literals on JRuby tree_rewriter.replace(gemspec_block.loc.expression, <<~GEMSPEC_BLOCK.chomp) #{source[0..-5]} #{dependency_source_code} #{source[-3..]} GEMSPEC_BLOCK # :nocov: end # The source code for the updated dependency declaration # @param existing_dependency [DependencyNode] The existing dependency # @return [String] The source code for the dependency declaration # @api private def dependency_source_code(existing_dependency = nil) # Use existing quote character for string literals q = new_quote_char(existing_dependency) # :nocov: supress false reporting of no coverage of multiline string literals on JRuby "#{receiver_name}.#{new_method_name(existing_dependency)} " \ "#{q}#{new_dependency.gem_name}#{q}, " \ "#{new_dependency.version_constraints.map { |vc| "#{q}#{vc}#{q}" }.join(', ')}" # :nocov: end # Use the same quote char as the existing dependency or default to single quote # @param existing_dependency [DependencyNode, nil] The existing dependency being updated # @return [String] The quote character to use # @api private def new_quote_char(existing_dependency) if existing_dependency existing_dependency.node.children[3].loc.expression.source[0] else "'" end end # The method to use for the new dependency # # If `existing_dependency` is given and the dependency type (runtime vs. # development) matches, the existing dependency method is used. Otherwise, # the new_dependency method is used. # # The purpose of this method is ensure that an #add_dependency call is not # replaced with an #add_runtime_dependency call or vice versa. This # maintains consistency within the user's gemspec even though these methods # are functionally equivalent. # # @param existing_dependency [DependencyNode, nil] The existing dependency being updated # @return [Symbol] The method to use for the new dependency # @api private def new_method_name(existing_dependency) if existing_dependency && dependency_type_match?(existing_dependency) existing_dependency.dependency.method_name else new_dependency.method_name end end end |
Instance Method Details
#add_dependency ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Add the new_dependency to the end of the Gem::Specification block
98 99 100 101 102 103 104 105 106 107 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 98 def add_dependency # Add the dependency to the end of the Gem::Specification block internal_block = gemspec_block.children[2] if internal_block tree_rewriter.insert_after(internal_block.children.last.loc.expression, "\n #{dependency_source_code}") else # When the Gem::Specification block is empty, it require special handling add_dependency_to_empty_gemspec_block end end |
#add_dependency_to_empty_gemspec_block ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Add new_dependency to an empty Gem::Specification block
147 148 149 150 151 152 153 154 155 156 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 147 def add_dependency_to_empty_gemspec_block source = gemspec_block.loc.expression.source # :nocov: supress false reporting of no coverage of multiline string literals on JRuby tree_rewriter.replace(gemspec_block.loc.expression, <<~GEMSPEC_BLOCK.chomp) #{source[0..-5]} #{dependency_source_code} #{source[-3..]} GEMSPEC_BLOCK # :nocov: end |
#call(new_dependency) ⇒ void
This method returns an undefined value.
Adds or updates a dependency to the Gem::Specification block
72 73 74 75 76 77 78 79 80 81 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 72 def call(new_dependency) @new_dependency = new_dependency matching_dependencies = dependencies.select { |d| d.dependency.gem_name == new_dependency.gem_name } if matching_dependencies.any? update_dependencies(matching_dependencies) else add_dependency end end |
#dependency_method_to_type(method) ⇒ Symbol
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The dependency type (:runtime or :development) based on a given method name
129 130 131 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 129 def dependency_method_to_type(method) method == :add_development_dependency ? :development : :runtime end |
#dependency_source_code(existing_dependency = nil) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The source code for the updated dependency declaration
162 163 164 165 166 167 168 169 170 171 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 162 def dependency_source_code(existing_dependency = nil) # Use existing quote character for string literals q = new_quote_char(existing_dependency) # :nocov: supress false reporting of no coverage of multiline string literals on JRuby "#{receiver_name}.#{new_method_name(existing_dependency)} " \ "#{q}#{new_dependency.gem_name}#{q}, " \ "#{new_dependency.version_constraints.map { |vc| "#{q}#{vc}#{q}" }.join(', ')}" # :nocov: end |
#dependency_type_conflict_error(existing_dependency) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Error message for a dependency type conflict
113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 113 def dependency_type_conflict_error(existing_dependency) # :nocov: JRuby give false positive for this line being uncovered by tests <<~MESSAGE.chomp.gsub("\n", ' ') Trying to add a #{dependency_method_to_type(new_dependency.method_name).upcase} dependency on "#{new_dependency.gem_name}" which conflicts with the existing #{dependency_method_to_type(existing_dependency.dependency.method_name).upcase} dependency. MESSAGE # :nocov: end |
#dependency_type_match?(existing_dependency) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Checks if the new dependency type is the same as the existing dependency type
138 139 140 141 142 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 138 def dependency_type_match?(existing_dependency) # Either both are :add_development_dependency or both are not (existing_dependency.dependency.method_name == :add_development_dependency) == (new_dependency.method_name == :add_development_dependency) end |
#new_method_name(existing_dependency) ⇒ Symbol
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The method to use for the new dependency
If ‘existing_dependency` is given and the dependency type (runtime vs. development) matches, the existing dependency method is used. Otherwise, the new_dependency method is used.
The purpose of this method is ensure that an #add_dependency call is not replaced with an #add_runtime_dependency call or vice versa. This maintains consistency within the user’s gemspec even though these methods are functionally equivalent.
199 200 201 202 203 204 205 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 199 def new_method_name(existing_dependency) if existing_dependency && dependency_type_match?(existing_dependency) existing_dependency.dependency.method_name else new_dependency.method_name end end |
#new_quote_char(existing_dependency) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Use the same quote char as the existing dependency or default to single quote
177 178 179 180 181 182 183 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 177 def new_quote_char(existing_dependency) if existing_dependency existing_dependency.node.children[3].loc.expression.source[0] else "'" end end |
#update_dependencies(matching_dependencies) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Update the version constraint of the existing dependency(s)
87 88 89 90 91 92 93 |
# File 'lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb', line 87 def update_dependencies(matching_dependencies) matching_dependencies.each do |found_dependency| raise(dependency_type_conflict_error(found_dependency)) unless dependency_type_match?(found_dependency) tree_rewriter.replace(found_dependency.node.loc.expression, dependency_source_code(found_dependency)) end end |