├── .gitignore ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── cocoapods_links.gemspec ├── lib ├── cocoapods_links.rb ├── cocoapods_plugin.rb └── pod │ ├── command │ ├── link.rb │ ├── list.rb │ └── unlink.rb │ ├── links.rb │ ├── lockfile.rb │ └── pod.rb └── spec ├── command ├── link_spec.rb ├── list_spec.rb └── unlink_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :development do 6 | gem 'rake' 7 | gem 'bacon' 8 | gem 'mocha' 9 | gem 'mocha-on-bacon' 10 | end 11 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | cocoapods-links (0.3.0) 5 | cocoapods (~> 1.0) 6 | json (~> 1.8) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activesupport (4.2.6) 12 | i18n (~> 0.7) 13 | json (~> 1.7, >= 1.7.7) 14 | minitest (~> 5.1) 15 | thread_safe (~> 0.3, >= 0.3.4) 16 | tzinfo (~> 1.1) 17 | bacon (1.2.0) 18 | claide (1.0.0) 19 | cocoapods (1.0.0) 20 | activesupport (>= 4.0.2) 21 | claide (>= 1.0.0, < 2.0) 22 | cocoapods-core (= 1.0.0) 23 | cocoapods-deintegrate (>= 1.0.0, < 2.0) 24 | cocoapods-downloader (>= 1.0.0, < 2.0) 25 | cocoapods-plugins (>= 1.0.0, < 2.0) 26 | cocoapods-search (>= 1.0.0, < 2.0) 27 | cocoapods-stats (>= 1.0.0, < 2.0) 28 | cocoapods-trunk (>= 1.0.0, < 2.0) 29 | cocoapods-try (>= 1.0.0, < 2.0) 30 | colored (~> 1.2) 31 | escape (~> 0.0.4) 32 | fourflusher (~> 0.3.0) 33 | molinillo (~> 0.4.5) 34 | nap (~> 1.0) 35 | xcodeproj (>= 1.0.0, < 2.0) 36 | cocoapods-core (1.0.0) 37 | activesupport (>= 4.0.2) 38 | fuzzy_match (~> 2.0.4) 39 | nap (~> 1.0) 40 | cocoapods-deintegrate (1.0.0) 41 | cocoapods-downloader (1.0.0) 42 | cocoapods-plugins (1.0.0) 43 | nap 44 | cocoapods-search (1.0.0) 45 | cocoapods-stats (1.0.0) 46 | cocoapods-trunk (1.0.0) 47 | nap (>= 0.8, < 2.0) 48 | netrc (= 0.7.8) 49 | cocoapods-try (1.0.0) 50 | colored (1.2) 51 | escape (0.0.4) 52 | fourflusher (0.3.0) 53 | fuzzy_match (2.0.4) 54 | i18n (0.7.0) 55 | json (1.8.3) 56 | metaclass (0.0.4) 57 | minitest (5.9.0) 58 | mocha (1.1.0) 59 | metaclass (~> 0.0.1) 60 | mocha-on-bacon (0.2.2) 61 | mocha (>= 0.13.0) 62 | molinillo (0.4.5) 63 | nap (1.1.0) 64 | netrc (0.7.8) 65 | rake (10.4.2) 66 | thread_safe (0.3.5) 67 | tzinfo (1.2.2) 68 | thread_safe (~> 0.1) 69 | xcodeproj (1.0.0) 70 | activesupport (>= 3) 71 | claide (>= 1.0.0, < 2.0) 72 | colored (~> 1.2) 73 | 74 | PLATFORMS 75 | ruby 76 | 77 | DEPENDENCIES 78 | bacon 79 | bundler (~> 1.6) 80 | cocoapods-links! 81 | mocha 82 | mocha-on-bacon 83 | rake 84 | 85 | BUNDLED WITH 86 | 1.10.6 87 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Mike Owens 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cocoapods-links 2 | 3 | A CocoaPods plugin to manage local development pods 4 | 5 | ## Installation 6 | 7 | ```bash 8 | gem install cocoapods-links 9 | ``` 10 | 11 | ## Purpose 12 | Let's face it, pod development with local dependencies can be a pain. Let's say you have a project 13 | `MyApp` a few pods declared in your `Podfile`: 14 | 15 | ```ruby 16 | pod 'Foo', '~> 1.0.0' 17 | pod 'Bar', :git => 'https://github.com/MyCompany/Bar.git', :tag => "1.0.1" 18 | ``` 19 | 20 | Perhaps you need to make some modifications to `Bar` to implement a new feature in `MyApp`. So 21 | you modify your `Podfile`: 22 | 23 | ```ruby 24 | pod 'Bar', :path => "/path/to/bar/checkout" 25 | ``` 26 | This development flow requires you to make a temporary change to your `Podfile` 27 | that is managed by source control. Wouldn't it be great if CocoaPods offered a means to manage 28 | development pods without having to alter files under source control? 29 | 30 | Enter cocoapods-links. 31 | 32 | With cocoapods-links, developers can easily test their pods using the provided link functionality. 33 | Linking is a two-step process: 34 | 35 | Using `pod link` in a project folder will register a global link. Then, in another pod, 36 | `pod link ` will create a link to the registered pod as a development pod. 37 | 38 | This allows developers to easily test a pod because changes will be reflected immediately. 39 | When the link is no longer necessary, simply remove it with `pod unlink `. 40 | 41 | **NOTE:** Although the `Podfile` and `Podfile.lock` will not be updated using links, the Pods xcodeproj will be updated. If you check in the contents of your Pods directory then you must make sure 42 | to unlink your development pods prior to committing any changes. 43 | 44 | ## Usage 45 | 46 | #### Register 47 | To register a pod for local development linking: 48 | 49 | ```bash 50 | pod link 51 | ``` 52 | 53 | #### Unregister 54 | To unregister a pod: 55 | 56 | ```bash 57 | pod unlink 58 | ``` 59 | 60 | #### Link 61 | To link a pod for use in another pod project: 62 | 63 | ```bash 64 | pod link 65 | ``` 66 | 67 | #### Unlink 68 | To unlink a pod from a pod project: 69 | 70 | ```bash 71 | pod unlink 72 | ``` 73 | 74 | #### List 75 | To list all registered pods 76 | 77 | ```bash 78 | pod list links 79 | ``` 80 | 81 | To list all linked pods in a pod project: 82 | 83 | ```bash 84 | pod list links --linked 85 | ``` 86 | 87 | ### License 88 | 89 | cocoapods-links is released under the MIT license. See [LICENSE](LICENSE). 90 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | 3 | task :default => :spec 4 | 5 | task :spec do 6 | title 'Running Specs' 7 | files = FileList['spec/**/*_spec.rb'].shuffle.join(' ') 8 | sh "bundle exec bacon #{files}" 9 | end 10 | 11 | def title(title) 12 | cyan_title = "\033[0;36m#{title}\033[0m" 13 | puts 14 | puts '-' * 80 15 | puts cyan_title 16 | puts '-' * 80 17 | puts 18 | end 19 | -------------------------------------------------------------------------------- /cocoapods_links.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'cocoapods_links.rb' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'cocoapods-links' 8 | spec.version = CocoapodsLinks::VERSION 9 | spec.authors = ['Mike Owens'] 10 | spec.email = ['mike.owens11@gmail.com'] 11 | spec.summary = 'A CocoaPods plugin for linking and unlinking local pods for local development' 12 | spec.description = <<-DESC 13 | This CocoaPods plugin linking functionality allows to easily test their pods. 14 | 15 | This plugin adds the following commands to the CococPods command line: 16 | 17 | * pod link 18 | * pod unlink 19 | * pod list links 20 | 21 | DESC 22 | spec.homepage = 'https://github.com/mowens/cocoapods-links' 23 | spec.license = 'MIT' 24 | 25 | spec.files = `git ls-files`.split($/) 26 | spec.test_files = spec.files.grep(%r{^spec/}) 27 | spec.require_paths = ['lib'] 28 | 29 | spec.add_dependency 'cocoapods', '~> 1.0' 30 | spec.add_dependency 'json', '~> 1.8' 31 | 32 | spec.add_development_dependency 'bundler', '~> 1.6' 33 | spec.add_development_dependency 'rake', '~> 10.4' 34 | end 35 | -------------------------------------------------------------------------------- /lib/cocoapods_links.rb: -------------------------------------------------------------------------------- 1 | module CocoapodsLinks 2 | VERSION = '0.3.0' 3 | end 4 | -------------------------------------------------------------------------------- /lib/cocoapods_plugin.rb: -------------------------------------------------------------------------------- 1 | require 'pod/pod' 2 | require 'pod/lockfile' 3 | require 'pod/command/link' 4 | require 'pod/command/unlink' 5 | require 'pod/command/list' 6 | -------------------------------------------------------------------------------- /lib/pod/command/link.rb: -------------------------------------------------------------------------------- 1 | require 'pod/links' 2 | 3 | module Pod 4 | class Command 5 | class Link < Command 6 | self.summary = 'Create pod links for local pod development' 7 | self.description = <<-DESC 8 | The link functionality allows developers to easily test their pods. 9 | Linking is a two-step process: 10 | 11 | Using 'pod link' in a project folder will create a global link. 12 | Then, in some other pod, 'pod link ' will create a link to 13 | the local pod as a Development pod. 14 | 15 | This allows to easily test a pod because changes will be reflected immediately. 16 | When the link is no longer necessary, simply remove it with 'pod unlink '. 17 | DESC 18 | 19 | self.arguments = [ 20 | CLAide::Argument.new('POD_NAME', false) 21 | ] 22 | 23 | def initialize(argv) 24 | @pod = argv.shift_argument() 25 | super 26 | end 27 | 28 | # 29 | # if no pod is given from the command line then we will create a link for the current pod 30 | # so other pods can link it as a development dependency 31 | # 32 | # if a pod name is given from the command line then we will link that pod into the current 33 | # pod as a development dependency 34 | # 35 | def run 36 | unless @pod.nil? 37 | Pod::Command::Links.link @pod 38 | else 39 | Pod::Command::Links.register 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/pod/command/list.rb: -------------------------------------------------------------------------------- 1 | require 'pod/links' 2 | 3 | module Pod 4 | class Command 5 | class List 6 | class Links < List 7 | 8 | self.summary = 'List links' 9 | self.description = <<-DESC 10 | List the registered links 11 | DESC 12 | 13 | def self.options 14 | [[ 15 | '--linked', 'List pods linked in the current project' 16 | ]].concat(super) 17 | end 18 | 19 | def initialize(argv) 20 | @linked = argv.flag?('linked') 21 | super 22 | end 23 | 24 | def run 25 | Pod::Command::Links.list @linked 26 | end 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/pod/command/unlink.rb: -------------------------------------------------------------------------------- 1 | require 'pod/links' 2 | 3 | module Pod 4 | class Command 5 | class Unlink < Command 6 | self.summary = 'Remove pod links' 7 | self.description = <<-DESC 8 | The unlink functionality allows developers to remove reference to their local pods 9 | when they are finished testing 10 | 11 | Using 'pod unlink' in a project folder will remove the global link. 12 | 13 | Using 'pod unlink ' will remove the link to the developement pod 14 | and install the pod configured in the Podfile 15 | 16 | This allows to easily remove developement pod references 17 | DESC 18 | 19 | self.arguments = [ 20 | CLAide::Argument.new('POD_NAME', false) 21 | ] 22 | 23 | def initialize(argv) 24 | @pod = argv.shift_argument() 25 | super 26 | end 27 | 28 | # 29 | # if no pod is given from the command line then we will unregister the pod from the 30 | # registered links 31 | # 32 | # if a pod name is given from the command line then we will unlink the given pod from 33 | # the project 34 | # 35 | def run 36 | unless @pod.nil? 37 | Pod::Command::Links.unlink @pod 38 | else 39 | Pod::Command::Links.unregister 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/pod/links.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'fileutils' 3 | 4 | module Pod 5 | class Command 6 | 7 | # 8 | # Links utility that provides functionality around managing CocoaPod links 9 | # 10 | module Links 11 | 12 | # 13 | # Defines the path where the links database is stored (e.g. from pod link) 14 | # 15 | REGISTERED_DB = File.expand_path('~/.cocoapods/plugins/link/registered.json') 16 | 17 | # 18 | # Defines the path where per pod links are stored (e.g. from pod link command) 19 | # 20 | LINKED_DB = File.expand_path('~/.cocoapods/plugins/link/linked.json') 21 | 22 | # 23 | # Register a pod for local development in the current working directory. This working 24 | # directory must have a .podspec defining the pod 25 | # 26 | def self.register 27 | self.print "Registering '#{self.podspec.name}' > #{Dir.pwd}" 28 | self.write_db(REGISTERED_DB, self.registerd_db, { 29 | self.podspec.name => { 30 | "path" => Dir.pwd 31 | } 32 | }) 33 | end 34 | 35 | # 36 | # Unregister a pod 37 | # 38 | def self.unregister 39 | self.print "Unregistering '#{self.podspec.name}' > #{Dir.pwd}" 40 | db = self.registerd_db 41 | db.delete(self.podspec.name) 42 | self.write_db(REGISTERED_DB, db) 43 | end 44 | 45 | # 46 | # Creates a link for the given pod into the current project. The pod must be registered 47 | # using `pod link` 48 | # 49 | # @param pod the name of the pod to link into the current project 50 | # 51 | def self.link(pod) 52 | # only allow registered links to be used 53 | registered_link = self.get_registered_link pod 54 | if registered_link.nil? 55 | Command::help! "Pod '#{pod}'' is not registered. Did you run `pod link` from the #{pod} directory?" 56 | end 57 | 58 | # add the linked pod 59 | linked_pods = [pod] 60 | if self.linked_db.has_key?(Dir.pwd) 61 | linked_pods = linked_pods.concat self.linked_db[Dir.pwd]['pods'] 62 | end 63 | 64 | self.print "Adding link to '#{pod}' > #{registered_link['path']}" 65 | self.write_db(LINKED_DB, self.linked_db, { 66 | Dir.pwd => { 67 | 'pods' => linked_pods.uniq 68 | } 69 | }) 70 | 71 | # install pod from link 72 | Pod::Command::Install.run(CLAide::ARGV.new ["--no-repo-update"]) 73 | end 74 | 75 | # 76 | # Will unlink the give pod from the current pod project 77 | # 78 | # @param pod the name of the pod to unlink 79 | # 80 | def self.unlink(pod) 81 | if self.linked_db.has_key?(Dir.pwd) 82 | linked_pods = self.linked_db[Dir.pwd]['pods'] 83 | linked_pods.delete(pod) 84 | 85 | # 86 | # Update databased based on link state 87 | # if links exist, update list of links 88 | # if links do not exist, remove entry 89 | # 90 | self.print "Removing link to '#{pod}'" 91 | if linked_pods.empty? 92 | db = self.linked_db 93 | db.delete(Dir.pwd) 94 | self.write_db(LINKED_DB, db) 95 | else 96 | self.write_db(LINKED_DB, self.linked_db, { 97 | Dir.pwd => { 98 | 'pods' => linked_pods 99 | } 100 | }) 101 | end 102 | 103 | # install pod from repo 104 | Pod::Command::Install.run(CLAide::ARGV.new []) 105 | end 106 | end 107 | 108 | # 109 | # Entry point for the `pod` hook to check if the current pod project should use a linked pod 110 | # of installed from the pod requirements. In order for a link to be returned the following 111 | # must hold true: 112 | # 113 | # 1. The pod must be registered (e.g. pod link) 114 | # 2. The current pod project must have linked the registered link (e.g. pod link ) 115 | # 116 | # @param name the name of the pod to find a link for 117 | # 118 | # @returns the registered link for the given name or nil 119 | # 120 | def self.get_link(name) 121 | if self.linked_pods.include?(name) 122 | return self.get_registered_link name 123 | end 124 | return nil 125 | end 126 | 127 | # 128 | # List the links. 129 | # 130 | # - If linked is true then list the linked pods in the current project 131 | # - Id linked is false then list the registered links 132 | # 133 | # @param linked flag to determine which links to list 134 | # 135 | def self.list(linked = false) 136 | if linked 137 | self.print "Linked pods:" 138 | self.linked_pods.each do |pod| 139 | self.print "* #{pod}" 140 | end 141 | else 142 | self.print "Registered pods:" 143 | self.registerd_db.each do |pod, link| 144 | self.print "* #{pod} > #{link['path']}" 145 | end 146 | end 147 | end 148 | 149 | # 150 | # Get list of pods that are linked for the current pod project 151 | # 152 | # @return an array of installed links 153 | # 154 | def self.installed_links 155 | installed = [] 156 | self.linked_pods.each do |pod| 157 | unless self.get_registered_link(pod).nil? 158 | installed.append(pod) 159 | end 160 | end 161 | return installed 162 | end 163 | 164 | # 165 | # Prints a formatted message with the Pod Links prefix 166 | # 167 | def self.print(message) 168 | UI.puts("Pod #{'Links'.cyan} #{message}") 169 | end 170 | 171 | private 172 | 173 | # 174 | # Retrieve the registered links database from disk 175 | # 176 | # @returns the registered links database 177 | # 178 | def self.registerd_db 179 | if File.exists?(REGISTERED_DB) 180 | return JSON.parse(File.read(REGISTERED_DB)) 181 | end 182 | return {} 183 | end 184 | 185 | # 186 | # Retrieve the linked database from disk 187 | # 188 | # @returns the linked database 189 | # 190 | def self.linked_db 191 | if File.exists?(LINKED_DB) 192 | return JSON.parse(File.read(LINKED_DB)) 193 | end 194 | return {} 195 | end 196 | 197 | # 198 | # Retrieve a link for the given name from the database. If the link does not exist in the 199 | # for the given name then this will return nil 200 | # 201 | # @param name the name of the link to retrieve from the database 202 | # 203 | # @return the link for the given name or nil 204 | # 205 | def self.get_registered_link(name) 206 | if self.registerd_db.has_key?(name) 207 | return self.registerd_db[name] 208 | end 209 | return nil 210 | end 211 | 212 | # 213 | # Retrieve the names of the linked pods for the current project (e.g. the current directory) 214 | # 215 | # @returns a list of pods that are linked for the current project 216 | # 217 | def self.linked_pods 218 | if self.linked_db.has_key?(Dir.pwd) 219 | return self.linked_db[Dir.pwd]['pods'] 220 | end 221 | return [] 222 | end 223 | 224 | # 225 | # Read the podspec in the current working directory 226 | # 227 | # @returns the podspec 228 | # 229 | def self.podspec 230 | spec = Dir["#{Dir.pwd}/*.podspec"] 231 | if spec.empty? 232 | help! 'A .podspec must exist in the directory `pod link` is ran' 233 | end 234 | return Specification.from_file(spec.fetch(0)) 235 | end 236 | 237 | # 238 | # Will write the provided database to disk with the newly provided link content 239 | # 240 | # @param filename the name of the file to write the links to 241 | # @param links the content to write to disk 242 | # 243 | def self.write_db(db_path, db, entry = {}) 244 | dirname = File.dirname(db_path) 245 | unless File.directory?(dirname) 246 | FileUtils.mkdir_p(dirname) 247 | end 248 | File.open(db_path,'w') do |f| 249 | f.write(JSON.pretty_generate(db.merge(entry))) 250 | end 251 | end 252 | end 253 | end 254 | end 255 | -------------------------------------------------------------------------------- /lib/pod/lockfile.rb: -------------------------------------------------------------------------------- 1 | require 'pod/links' 2 | 3 | # 4 | # Logic: 5 | # Override of the Lockfile generation to filter out linked pods in favor of their previously 6 | # installed state (e.g. the state reflected from the Podfile). This is somewhat brittle as it 7 | # depends on the format of the Lockfile hash contents. If the format changes, then this will also 8 | # need to be changed. It would be far better to integrate the link filtering elsewhere but this 9 | # works "for now" 10 | # 11 | 12 | module Pod 13 | class Lockfile 14 | 15 | PODFILE_LOCK = "Podfile.lock" 16 | 17 | alias_method :real_write_to_disk, :write_to_disk 18 | 19 | # 20 | # Hook the Podfile.lock file generation to allow us to filter out the links added to the 21 | # Podfile.lock. The logic here is to replace the new Podfile.lock link content with what existed 22 | # before the link was added. Currently, this is called for both Podfile.lock and Manifest.lock 23 | # file so we only want to alter the Podfile.lock 24 | # 25 | # @param path path to write the .lock file to 26 | # 27 | def write_to_disk(path) 28 | 29 | # code here mimics the original method but with link filtering 30 | filename = File.basename(path) 31 | path.dirname.mkpath unless path.dirname.exist? 32 | yaml = to_link_yaml 33 | File.open(path, 'w') { |f| f.write(yaml) } 34 | self.defined_in_file = path 35 | end 36 | 37 | # 38 | # Will create pretty print YAML stringfrom the links hash that is to be dumped to a Podfile.lock 39 | # 40 | # This code is identical to `to_yaml` except we pass the `to_link_hash` instead of the `to_hash` 41 | # 42 | # @returns the YAML string content to dump to a Podfile.lock without link content 43 | # 44 | def to_link_yaml 45 | keys_hint = [ 46 | 'PODS', 47 | 'DEPENDENCIES', 48 | 'EXTERNAL SOURCES', 49 | 'CHECKOUT OPTIONS', 50 | 'SPEC CHECKSUMS', 51 | 'COCOAPODS', 52 | ] 53 | YAMLHelper.convert_hash(to_link_hash, keys_hint, "\n\n") 54 | end 55 | 56 | # 57 | # Will get the Podfile.lock contents hash after replacing the linked content with its previous 58 | # Podfile.lock information keeping the Podfile and Podfile.lock in sync and clear of any link 59 | # data 60 | # 61 | # @returns hash that is to be dumped to the Podfile.lock file without link content 62 | # 63 | def to_link_hash 64 | 65 | # retrieve the lock contents with links 66 | after_hash = to_hash 67 | 68 | unless File.exists?(PODFILE_LOCK) 69 | return after_hash 70 | end 71 | 72 | # retrieve the lock content before the links 73 | before_hash = YAML.load(File.read(PODFILE_LOCK)) 74 | 75 | # retrieve installed links 76 | links = Pod::Command::Links.installed_links 77 | 78 | # 79 | # Logic: 80 | # Here we will replace anything that changed in the contents that will be dumped in the 81 | # Podfile.lock due to links with the data that previously exists in the Podfile.lock. This 82 | # allows the Podfile.lock with the dependency trees to remain unchanged when linking 83 | # developement pods. The Podfile.lock contains several keys, but we only need to alter the 84 | # following: 85 | # 86 | # - PODS 87 | # - DEPENDENCIES 88 | # - EXTERNAL SOURCES 89 | # - CHECKOUT OPTIONS 90 | # - SPEC CHECKSUMS 91 | # 92 | after_hash['PODS'] = 93 | merge_pods links, before_hash['PODS'], after_hash['PODS'] 94 | 95 | after_hash['DEPENDENCIES'] = 96 | merge_dependencies links, before_hash['DEPENDENCIES'], after_hash['DEPENDENCIES'] 97 | 98 | after_hash['EXTERNAL SOURCES'] = 99 | merge_hashes links, before_hash['EXTERNAL SOURCES'], after_hash['EXTERNAL SOURCES'] 100 | 101 | after_hash['CHECKOUT OPTIONS'] = 102 | merge_hashes links, before_hash['CHECKOUT OPTIONS'], after_hash['CHECKOUT OPTIONS'] 103 | 104 | after_hash['SPEC CHECKSUMS'] = 105 | merge_hashes links, before_hash['SPEC CHECKSUMS'], after_hash['SPEC CHECKSUMS'] 106 | 107 | return after_hash 108 | end 109 | 110 | def merge_pods(links, before, after) 111 | links.each do |link| 112 | before_index = find_pod_index before, link 113 | after_index = find_pod_index after, link 114 | unless before_index.nil? || after_index.nil? 115 | 116 | # get previous value 117 | after_value = after[after_index] 118 | 119 | # update new value 120 | after[after_index] = before[before_index] 121 | 122 | # iterate and update all dependencies of previous value 123 | if after_value.is_a?(Hash) 124 | 125 | # clean all deps that may have been added as new deps 126 | after_value[after_value.keys[0]].each do |key| 127 | # key: CocoaLumberjack/Core or CocoaLumberjack/Extensions (= 1.9.2) 128 | key_desc = key.split(" (", 2)[0] 129 | 130 | inner_after_index = find_pod_index after, key_desc 131 | inner_before_index = find_pod_index before, key_desc 132 | 133 | unless inner_before_index.nil? && inner_after_index.nil? 134 | after[inner_after_index] = before[inner_before_index] 135 | else 136 | # if it was removed in the new deps 137 | unless before_index.nil? 138 | after.insert(before_index, before[before_index]) 139 | end 140 | end 141 | end 142 | end 143 | end 144 | end 145 | return after 146 | end 147 | 148 | # 149 | # Will merge the DEPENDENCIES of the Podfile.lock before a link and after a link 150 | # 151 | # @param links the installed links 152 | # @param before the DEPENDENCIES in the Podfile.lock before the link occurs 153 | # @param after the DEPENDENCIES after the link (includes new link that we want to filter out) 154 | # 155 | # @returns the merged DEPENDENCIES replacing any links that were added with their previous value 156 | # 157 | def merge_dependencies(links, before, after) 158 | links.each do |link| 159 | before_index = find_dependency_index before, link 160 | after_index = find_dependency_index after, link 161 | unless before_index.nil? || after_index.nil? 162 | after[after_index] = before[before_index] 163 | end 164 | end 165 | return after 166 | end 167 | 168 | # 169 | # Will merge the hashes of the Podfile.lock before a link and after a link 170 | # 171 | # @param links the installed links 172 | # @param before the hash in the Podfile.lock before the link occurs 173 | # @param after the hash after the link (includes new link that we want to filter out) 174 | # 175 | # @returns the merged hash replacing any links that were added with their previous value 176 | # 177 | def merge_hashes(links, before, after) 178 | if before.nil? 179 | return after 180 | end 181 | links.each do |link| 182 | if before.has_key?(link) 183 | after[link] = before[link] 184 | else 185 | if after.has_key?(link) 186 | after.delete(link) 187 | end 188 | end 189 | end 190 | return after 191 | end 192 | 193 | private 194 | 195 | # 196 | # Find the index in the pod array based on the link name. The pod array 197 | # also contains version/path information so we need to massage the pod value 198 | # for comparison. Pods are in the following format: 199 | # 200 | # Name (requirements) 201 | # 202 | # Example: 203 | # Alamofire (= 1.1.3) 204 | # 205 | # @param pods the array to search 206 | # @param name the name of the pod to find 207 | # 208 | # NOTE: the pods in the array can be strings or hashes, so we will check for both 209 | # 210 | # @return the index of nil 211 | # 212 | def find_pod_index(pods, name) 213 | pods.index { |pod| 214 | desc = pod 215 | if pod.is_a?(Hash) 216 | desc = pod.keys[0] 217 | end 218 | desc.split(" (", 2)[0] == name 219 | } 220 | end 221 | 222 | # 223 | # Find the index in the dependency array based on the link name. The dependency array 224 | # also contains version/path information so we need to massage the dependency value 225 | # for comparison. Dependencies are in the following format: 226 | # 227 | # Name (requirements) 228 | # 229 | # Example: 230 | # Alamofire (= 1.1.3) 231 | # Quick (from `https://github.com/Quick/Quick`, tag `v0.2.2`) 232 | # 233 | # @param dependencies the array to search 234 | # @param name the name of the dependency to find 235 | # 236 | # @returns the index of nil 237 | # 238 | def find_dependency_index(dependencies, name) 239 | dependencies.index { |dependency| 240 | dependency.split(" (", 2)[0] == name 241 | } 242 | end 243 | end 244 | end 245 | -------------------------------------------------------------------------------- /lib/pod/pod.rb: -------------------------------------------------------------------------------- 1 | require 'pod/links' 2 | 3 | # 4 | # In order to support installing pods from links we want to override the pod entry point 5 | # in the pod spec so we can lookup a link prior to installing the pod 6 | # 7 | module Pod 8 | class Podfile 9 | module DSL 10 | alias_method :real_pod, :pod 11 | def pod(name = nil, *requirements, &block) 12 | # 13 | # Logic: 14 | # Lookup a link for the given pod name. If a link exists then the pod will be installed 15 | # via the link instead of the provided requirements (e.g. it will setup local pod development 16 | # for the link). If the link does not exist, then the pod will be installed normally 17 | # 18 | 19 | # handle subspec link 20 | linked_name = name 21 | if name.include? "/" 22 | linked_name = name.split("/")[0] 23 | end 24 | link = Pod::Command::Links.get_link(linked_name) 25 | unless link.nil? 26 | Pod::Command::Links.print "Using link '#{name}' > #{link['path']}" 27 | real_pod(name, :path => link['path'], &block) 28 | else 29 | real_pod(name, *requirements, &block) 30 | end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/command/link_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | module Pod 4 | describe Command::Link do 5 | describe 'CLAide' do 6 | it 'registers itself' do 7 | Command.parse(['link']).should.be.instance_of Command::Link 8 | end 9 | end 10 | 11 | before do 12 | @command = Pod::Command::Link.new CLAide::ARGV.new [] 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/command/list_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | module Pod 4 | describe Command::List::Links do 5 | describe 'CLAide' do 6 | it 'registers itself' do 7 | Command.parse(%w(list links)).should.be.instance_of Command::List::Links 8 | end 9 | end 10 | 11 | before do 12 | @command = Pod::Command::List::Links.new CLAide::ARGV.new [] 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/command/unlink_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | module Pod 4 | describe Command::Unlink do 5 | describe 'CLAide' do 6 | it 'registers itself' do 7 | Command.parse(['unlink']).should.be.instance_of Command::Unlink 8 | end 9 | end 10 | 11 | before do 12 | @command = Pod::Command::Unlink.new CLAide::ARGV.new [] 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ROOT = Pathname.new(File.expand_path('../../', __FILE__)) 2 | $:.unshift((ROOT + 'lib').to_s) 3 | 4 | require 'cocoapods' 5 | require 'cocoapods_plugin' 6 | 7 | require 'mocha' 8 | require 'mocha-on-bacon' 9 | --------------------------------------------------------------------------------