claide-plugins 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,55 @@
1
+ # Bootstrap
2
+ #-----------------------------------------------------------------------------#
3
+
4
+ task :bootstrap do
5
+ if system('which bundle')
6
+ sh 'bundle install'
7
+ else
8
+ $stderr.puts "\033[0;31m" \
9
+ "[!] Please install the bundler gem manually:\n" \
10
+ ' $ [sudo] gem install bundler' \
11
+ "\e[0m"
12
+ exit 1
13
+ end
14
+ end
15
+
16
+ begin
17
+
18
+ require 'bundler/gem_tasks'
19
+
20
+ task :default => 'spec'
21
+
22
+ # Spec
23
+ #-----------------------------------------------------------------------------#
24
+
25
+ desc 'Runs all the specs'
26
+ task :spec do
27
+ start_time = Time.now
28
+ sh "bundle exec bacon #{specs('**')}"
29
+ duration = Time.now - start_time
30
+ puts "Tests completed in #{duration}s"
31
+ Rake::Task['rubocop'].invoke
32
+ end
33
+
34
+ def specs(dir)
35
+ FileList["spec/#{dir}/*_spec.rb"].shuffle.join(' ')
36
+ end
37
+
38
+ # Rubocop
39
+ #-----------------------------------------------------------------------------#
40
+
41
+ desc 'Checks code style'
42
+ task :rubocop do
43
+ require 'rubocop'
44
+ cli = RuboCop::CLI.new
45
+ result = cli.run(FileList['{spec,lib}/**/*.rb'])
46
+ abort('RuboCop failed!') unless result == 0
47
+ end
48
+
49
+
50
+ rescue LoadError
51
+ $stderr.puts "\033[0;31m" \
52
+ '[!] Some Rake tasks haven been disabled because the environment' \
53
+ ' couldn’t be loaded. Be sure to run `rake bootstrap` first.' \
54
+ "\e[0m"
55
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'claide_plugins.rb'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'claide-plugins'
8
+ spec.version = CLAidePlugins::VERSION
9
+ spec.authors = ['David Grandinetti', 'Olivier Halligon']
10
+ spec.summary = %q{CLAide plugin which shows info about available CLAide plugins.}
11
+ spec.description = <<-DESC
12
+ This CLAide plugin shows information about all available CLAide plugins
13
+ (yes, this is very meta!).
14
+ This plugin adds the "plugins" subcommand to a binary so that you can list
15
+ all plugins (registered in the reference JSON hosted at CocoaPods/cocoapods-plugins)
16
+ DESC
17
+ spec.homepage = 'https://github.com/cocoapods/claide-plugins'
18
+ spec.license = 'MIT'
19
+
20
+ spec.files = `git ls-files`.split($/)
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_runtime_dependency 'nap', '~> 1.0'
25
+ spec.add_runtime_dependency 'cork', '~> 0'
26
+ spec.add_runtime_dependency 'open4', '~> 1.3'
27
+
28
+ spec.add_development_dependency 'bundler', '~> 1.3'
29
+ spec.add_development_dependency 'rake'
30
+
31
+ spec.required_ruby_version = '>= 2.0.0'
32
+ end
@@ -0,0 +1,120 @@
1
+ require 'claide/command/gem_index_cache'
2
+
3
+ module CLAide
4
+ class Command
5
+ # This module is used by Command::PluginsHelper to download the Gem
6
+ # Specification data, check if a Gem is installed, and provide info
7
+ # on all versions of a Gem.
8
+ #
9
+ module GemHelper
10
+ # A GemIndexCache to manage downloading/caching the spec index.
11
+ #
12
+ @cache = nil
13
+
14
+ # Getter for GemIndexCache
15
+ #
16
+ # @return [GemIndexCache] a new or memoized GemIndexCache
17
+ #
18
+ def self.cache
19
+ @cache ||= GemIndexCache.new
20
+ end
21
+
22
+ # Instantiate a cache and download the spec index if it has
23
+ # not already been done.
24
+ #
25
+ def self.download_and_cache_specs
26
+ cache.download_and_cache_specs
27
+ end
28
+
29
+ # Tells if a gem is installed
30
+ #
31
+ # @param [String] gem_name
32
+ # The name of the plugin gem to test
33
+ #
34
+ # @param [String] version_string
35
+ # An optional version string, used to check if a specific
36
+ # version of a gem is installed
37
+ #
38
+ # @return [Bool] true if the gem is installed, false otherwise.
39
+ #
40
+ def self.gem_installed?(gem_name, version_string = nil)
41
+ version = Gem::Version.new(version_string) if version_string
42
+
43
+ if Gem::Specification.respond_to?(:find_all_by_name)
44
+ gems = Gem::Specification.find_all_by_name(gem_name)
45
+ return !gems.empty? unless version
46
+ gems.each { |gem| return true if gem.version == version }
47
+ false
48
+ else
49
+ dep = Gem::Dependency.new(gem_name, version_string)
50
+ !Gem.source_index.search(dep).empty?
51
+ end
52
+ end
53
+
54
+ # Get the version of a gem that is installed locally. If more than
55
+ # one version is installed, this returns the first version found,
56
+ # which MAY not be the highest/newest version.
57
+ #
58
+ # @return [String] The version of the gem that is installed,
59
+ # or nil if it is not installed.
60
+ #
61
+ def self.installed_version(gem_name)
62
+ if Gem::Specification.respond_to?(:find_all_by_name)
63
+ gem = Gem::Specification.find_all_by_name(gem_name).first
64
+ else
65
+ dep = Gem::Dependency.new(gem_name)
66
+ gem = Gem.source_index.search(dep).first
67
+ end
68
+ gem ? gem.version.to_s : nil
69
+ end
70
+
71
+ # Create a string containing all versions of a plugin,
72
+ # colored to indicate if a specific version is installed
73
+ # locally.
74
+ #
75
+ # @param [String] plugin_name
76
+ # The name of the plugin gem
77
+ #
78
+ # @param [GemIndexCache] index_cache
79
+ # Optional index cache can be passed in, otherwise
80
+ # the module instance is used.
81
+ #
82
+ # @return [String] a string containing a comma separated
83
+ # concatenation of all versions of a plugin
84
+ # that were found on rubygems.org
85
+ #
86
+ def self.versions_string(plugin_name, index_cache = @cache)
87
+ name_tuples = index_cache.specs_with_name(plugin_name)
88
+ sorted_versions = name_tuples.sort_by(&:version)
89
+ version_strings = colorize_versions(sorted_versions)
90
+ version_strings.join ', '
91
+ end
92
+
93
+ #----------------#
94
+
95
+ private
96
+
97
+ # Colorize an Array of version strings so versions that are installed
98
+ # are green and uninstalled versions are yellow.
99
+ #
100
+ # @param [Array] versions
101
+ # sorted array of Gem::NameTuples representing all versions of
102
+ # a plugin gem.
103
+ #
104
+ # @return [Array] An array of strings, each one being the version
105
+ # string of the same plugin
106
+ #
107
+ def self.colorize_versions(versions)
108
+ colored_strings = []
109
+ versions.reverse_each do |name_tuple|
110
+ if gem_installed?(name_tuple.name, name_tuple.version.to_s)
111
+ colored_strings << name_tuple.version.to_s.green
112
+ else
113
+ colored_strings << name_tuple.version.to_s.yellow
114
+ end
115
+ end
116
+ colored_strings
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,87 @@
1
+ require 'claide/command/gem_helper'
2
+
3
+ module CLAide
4
+ class Command
5
+ # This class is used by Command::GemsHelper to download the Gem
6
+ # Specification index from rubygems.org and provide info about
7
+ # the index.
8
+ #
9
+ class GemIndexCache
10
+ # A memoized hash of all the rubygem specs. If it is nil, the specs will
11
+ # be downloaded, which will take a few seconds to download.
12
+ #
13
+ # @return [Hash] The hash of all rubygems
14
+ #
15
+ def specs
16
+ @specs ||= download_specs
17
+ end
18
+
19
+ # Alias to make the initial caching process more readable.
20
+ #
21
+ alias_method :download_and_cache_specs, :specs
22
+
23
+ # Get an Array of Gem::NameTuple objects that match a given
24
+ # spec name.
25
+ #
26
+ # @param [String] name
27
+ # The name of the gem to match on (e.g. 'cocoapods-try')
28
+ #
29
+ # @return [Array] Array of Gem::NameTuple that match the name
30
+ #
31
+ def specs_with_name(name)
32
+ matching_specs = @specs.select do |spec|
33
+ spec[0].name == name
34
+ end
35
+
36
+ name_tuples = []
37
+ matching_specs.each do |(name_tuple, _)|
38
+ name_tuples << name_tuple
39
+ end
40
+
41
+ name_tuples
42
+ end
43
+
44
+ #----------------#
45
+
46
+ private
47
+
48
+ # Force the rubygem spec index file
49
+ #
50
+ # @return [Hash] The hash of all rubygems
51
+ #
52
+ def download_specs
53
+ UI.puts 'Downloading Rubygem specification index...'
54
+ fetcher = Gem::SpecFetcher.fetcher
55
+ results, errors = fetcher.available_specs(:released)
56
+
57
+ unless errors.empty?
58
+ UI.puts 'Error downloading Rubygem specification index: ' +
59
+ errors.first.error.to_s
60
+ return []
61
+ end
62
+
63
+ flatten_fetcher_results(results)
64
+ end
65
+
66
+ # Flatten the dictionary returned from Gem::SpecFetcher
67
+ # to a simple array.
68
+ #
69
+ # @param [Hash] results
70
+ # the hash returned from the call to
71
+ # Gem::SpecFetcher.available_specs()
72
+ #
73
+ # @return [Array] Array of all spec results
74
+ #
75
+ def flatten_fetcher_results(results)
76
+ specs = []
77
+ results.each do |source, source_specs|
78
+ source_specs.each do |tuple|
79
+ specs << [tuple, source]
80
+ end
81
+ end
82
+
83
+ specs
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,47 @@
1
+ require 'rest'
2
+ require 'json'
3
+ require 'cork'
4
+ require 'claide'
5
+ require 'claide/command/plugins_config'
6
+
7
+ UI = Cork::Board.new
8
+
9
+ module CLAide
10
+ module Plugins
11
+ class << self
12
+ attr_accessor :config
13
+ end
14
+ # set a default configuration that will work with claide-plugins
15
+ self.config = Configuration.new
16
+ end
17
+
18
+ # Indicates a runtime error **not** caused by a bug.
19
+ #
20
+ class PlainInformative < StandardError; end
21
+
22
+ # Indicates a user error.
23
+ #
24
+ class Informative < PlainInformative; end
25
+
26
+ class Command
27
+ # The claide plugins command.
28
+ #
29
+ class Plugins < Command
30
+ require 'claide/command/plugins/list'
31
+ require 'claide/command/plugins/search'
32
+ require 'claide/command/plugins/create'
33
+
34
+ self.abstract_command = true
35
+ self.default_subcommand = 'list'
36
+
37
+ self.summary = 'Show available plugins'
38
+ self.description = <<-DESC
39
+ Lists or searches the available plugins
40
+ and show if you have them installed or not.
41
+
42
+ Also allows you to quickly create a new
43
+ plugin using a provided template.
44
+ DESC
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,121 @@
1
+ require 'claide/command/plugins_helper'
2
+ require 'claide/executable'
3
+
4
+ module CLAide
5
+ class Command
6
+ class Plugins
7
+ # The create subcommand. Used to create a new plugin using either the
8
+ # default template (CocoaPods/cocoapods-plugin-template) or a custom
9
+ # template
10
+ #
11
+ class Create < Plugins
12
+ NAME_PREFIX = 'claide-'
13
+
14
+ self.summary = 'Creates a new plugin'
15
+ self.description = <<-DESC
16
+ Creates a scaffold for the development of a new plugin
17
+ named `NAME` according to the CocoaPods best practices.
18
+
19
+ If a `TEMPLATE_URL`, pointing to a git repo containing a
20
+ compatible template, is specified, it will be used
21
+ in place of the default one.
22
+ DESC
23
+
24
+ self.arguments = [
25
+ CLAide::Argument.new('NAME', true),
26
+ CLAide::Argument.new('TEMPLATE_URL', false),
27
+ ]
28
+
29
+ def initialize(argv)
30
+ @name = argv.shift_argument
31
+ unless @name.nil? || @name.empty? || @name.start_with?(NAME_PREFIX)
32
+ @name = NAME_PREFIX + @name.dup
33
+ end
34
+ @template_url = argv.shift_argument
35
+ super
36
+ end
37
+
38
+ def validate!
39
+ super
40
+ if @name.nil? || @name.empty?
41
+ help! 'A name for the plugin is required.'
42
+ end
43
+
44
+ help! 'The plugin name cannot contain spaces.' if @name.match(/\s/)
45
+ end
46
+
47
+ def run
48
+ clone_template
49
+ configure_template
50
+ show_reminder
51
+ end
52
+
53
+ #----------------------------------------#
54
+
55
+ private
56
+
57
+ # !@group Private helpers
58
+
59
+ extend CLAide::Executable
60
+ executable :git
61
+
62
+ TEMPLATE_BASE_URL = 'https://github.com/CocoaPods/'
63
+ TEMPLATE_REPO = TEMPLATE_BASE_URL + 'cocoapods-plugin-template.git'
64
+ TEMPLATE_INFO_URL = TEMPLATE_BASE_URL + 'cocoapods-plugin-template'
65
+
66
+ # Clones the template from the remote in the working directory using
67
+ # the name of the plugin.
68
+ #
69
+ # @return [void]
70
+ #
71
+ def clone_template
72
+ UI.section("-> Creating `#{@name}` plugin") do
73
+ UI.notice "using template '#{template_repo_url}'"
74
+ command = ['clone', template_repo_url, @name]
75
+ if method(:git!).arity == -1
76
+ git! command
77
+ else
78
+ # TODO: delete this conditional and use the other branch when
79
+ # 0.5.0 is released
80
+ require 'shellwords'
81
+ git! command.map(&:to_s).map(&:shellescape).join(' ')
82
+ end
83
+ end
84
+ end
85
+
86
+ # Runs the template configuration utilities.
87
+ #
88
+ # @return [void]
89
+ #
90
+ def configure_template
91
+ UI.section('-> Configuring template') do
92
+ Dir.chdir(@name) do
93
+ if File.file? 'configure'
94
+ system "./configure #{@name}"
95
+ else
96
+ UI.warn 'Template does not have a configure file.'
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ # Checks if a template URL is given else returns the TEMPLATE_REPO URL
103
+ #
104
+ # @return String
105
+ #
106
+ def template_repo_url
107
+ @template_url || TEMPLATE_REPO
108
+ end
109
+
110
+ # Shows a reminder to the plugin author to make a Pull Request
111
+ # in order to update plugins.json once the plugin is released
112
+ #
113
+ def show_reminder
114
+ repo = PluginsHelper.plugins_raw_url
115
+ UI.notice "Don't forget to create a Pull Request on #{repo}\n" \
116
+ ' to add your plugin to the plugins.json file once it is released!'
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end