klipp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7ad411833795ad014a927e4dc6851f5d4c2630d6
4
+ data.tar.gz: 85ae4b599bf421be5ec49b50ff4f9b7bc699585b
5
+ SHA512:
6
+ metadata.gz: d3ef430e9e4110b425a3da45dfef5ab577e65d7c24f73aac7ca6c32c8c97beb4deb5b9956731bf9affd09d50788b5b090724633fa5862ff51fb89a2643dfc350
7
+ data.tar.gz: df73b6338e8583b51dc78a855b532eb5e429eada147258db201b3b4b12d3e4b3c7a71fe75e267e3ec36221c985a727d23d032a1783599fd1c5c6bf54fd234d4b
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in klipp.gemspec
4
+ gemspec
5
+
6
+ gem 'coveralls', require: false
7
+ gem 'rspec'
8
+ gem 'mocha', :require => false
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Eric-Paul Lecluse
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.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # Klipp
2
+ ## Xcode templates for the rest of us.
3
+
4
+ Klipp is a command line gem for creating new (Xcode) projects from existing templates. Unlike Apple's private plist-based templating system, Klipp takes an existing Xcode project and creates a new project by copying and modifying an existing template project by your own specifications.
5
+
6
+ ## Installation
7
+
8
+ Install Klipp with RubyGems:
9
+
10
+ $ gem install klipp
11
+
12
+ Execute and read the usage instructions:
13
+
14
+ $ klipp
15
+
16
+ ## Usage
17
+
18
+ Klipp creates a template repository in your home directory, at ~/.klipp/templates
19
+ A template consists of a directory and an accompanying Yaml file, e.g.:
20
+
21
+ TemplateA
22
+ TemplateA.yml
23
+
24
+ Once you have templates like these present, you can prepare a new instance of that project by executing:
25
+
26
+ $ klipp prepare TemplateA
27
+
28
+ This will create a TemplateA.klippfile in your current directory, for you to edit with your favorite text editor.
29
+ From there, customize your project and when happy with the results, run:
30
+
31
+ $ klipp create
32
+
33
+ This will create a new directory called TemplateA, that contains your new project.
34
+
35
+ ## Known issues
36
+
37
+ * Creating templates is done manually, this will be automated
38
+ * Errors while parsing the .klippfile are not yet handled gracefully
39
+ * Highline support for on-the-fly creation of new projects is not yet enabled
40
+ * The root of newly created projects carries the same name as the template, this will be adjustable.
41
+
42
+ ## Contributing
43
+
44
+ 1. Fork it
45
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
46
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
47
+ 4. Push to the branch (`git push origin my-new-feature`)
48
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/klipp ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'klipp'
4
+
5
+ Klipp::Configuration.auto_create_dirs = true
6
+ Klipp.route(*ARGV)
data/build_klipp.sh ADDED
@@ -0,0 +1,6 @@
1
+ gem uninstall klipp -x
2
+ rbenv rehash
3
+ rm klipp-*
4
+ gem build klipp.gemspec
5
+ gem install klipp-*
6
+ rbenv rehash
data/klipp.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'klipp/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'klipp'
8
+ spec.version = Klipp::VERSION
9
+ spec.authors = ['Eric-Paul Lecluse']
10
+ spec.email = %w(e@epologee.com)
11
+ spec.description = 'Klipp, Xcode templates for the rest of us.'
12
+ spec.summary = "Klipp is a command line gem for creating new (Xcode) projects from existing templates. Unlike Apple's private plist-based templating system, Klipp takes an existing Xcode project and creates a new project by copying and modifying an existing template project by your own specifications."
13
+ spec.homepage = 'https://github.com/epologee/klipp'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = %w(lib)
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.3'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'highline'
24
+ spec.add_development_dependency 'ptools'
25
+ spec.add_development_dependency 'colored'
26
+ end
@@ -0,0 +1,17 @@
1
+ module BufferedOutput
2
+ module ClassMethods
3
+ @@output = nil
4
+
5
+ def output=(output)
6
+ @@output = output
7
+ end
8
+
9
+ def output
10
+ @@output || $stdout
11
+ end
12
+
13
+ def buffer_puts(output)
14
+ self.output.puts output
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ module Klipp
2
+
3
+ class Configuration
4
+ @@auto_create_dirs = false # set by the gem's binary
5
+
6
+ def self.auto_create_dirs
7
+ @@auto_create_dirs
8
+ end
9
+
10
+ def self.auto_create_dirs= auto_create_dirs
11
+ @@auto_create_dirs = auto_create_dirs
12
+ end
13
+
14
+ def self.auto_create(dir)
15
+ if auto_create_dirs && File.directory?(File.dirname dir) && !File.exists?(dir)
16
+ Dir.mkdir dir
17
+ end
18
+ dir
19
+ end
20
+
21
+ def self.root_dir
22
+ auto_create File.join(Dir.home, '.klipp')
23
+ end
24
+
25
+ def self.templates_dir
26
+ auto_create File.join(root_dir, 'templates')
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,19 @@
1
+ module Klipp
2
+ class ParameterList < Array
3
+ def options
4
+ select { |x| x.to_s[0, 1] == '-' }
5
+ end
6
+
7
+ def arguments
8
+ self - options
9
+ end
10
+
11
+ #def splice_option(name)
12
+ # !!delete(name)
13
+ #end
14
+
15
+ def shift_argument
16
+ (arg = arguments[0]) && delete(arg)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,46 @@
1
+ require 'ptools'
2
+ require 'fileutils'
3
+
4
+ module Klipp
5
+
6
+ class Project
7
+
8
+ def initialize(template)
9
+ @template = template
10
+ end
11
+
12
+ def create
13
+ # Check template directory exists
14
+ # Copy all files while adjusting paths
15
+ source_template_dir = File.join(Klipp::Configuration.templates_dir, @template.name)
16
+ target_template_dir = File.join(Dir.pwd, @template.name)
17
+
18
+ raise "Target directory already exists. Klipp will not overwrite your project: #{target_template_dir}" if File.exists? target_template_dir
19
+
20
+ @source_files = Dir.glob(File.join(source_template_dir, '**', '*'))
21
+ @source_files.each do |source_file|
22
+ transfer_file source_file, target_file(source_template_dir, source_file, target_template_dir)
23
+ end
24
+ end
25
+
26
+ def target_file(source_template_dir, source_file, target_template_dir)
27
+ stripped_path = source_file.gsub(source_template_dir, '')
28
+ customizable_path = @template.replace_tokens(stripped_path)
29
+ File.join(target_template_dir, customizable_path)
30
+ end
31
+
32
+ def transfer_file(source_file, target_file)
33
+ FileUtils.mkdir_p File.dirname(target_file)
34
+
35
+ if File.directory? source_file
36
+ FileUtils.mkdir_p target_file
37
+ elsif File.binary? source_file
38
+ FileUtils.cp(source_file, target_file)
39
+ else
40
+ IO.write target_file, @template.replace_tokens(File.read(source_file))
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,50 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+
4
+ module Klipp
5
+
6
+ class Template
7
+ attr_reader :tokens, :name
8
+
9
+ def initialize path, name
10
+ @name = name
11
+ raise 'Attempted to initialize template without name' unless name
12
+
13
+ full_path = File.join(path, "#{name}.yml")
14
+ raise "Unknown template name: #{name} (in template directory #{path})" unless File.exists?(full_path)
15
+ yaml_tokens = YAML.load(File.read(full_path))
16
+
17
+ @tokens = Hash[yaml_tokens.map { |token_name, values| [token_name, Klipp::Token.new(values)] }]
18
+ end
19
+
20
+ def [](name)
21
+ @tokens[name].value
22
+ end
23
+
24
+ def []=(name, value)
25
+ @tokens[name].value=value
26
+ end
27
+
28
+ def load_klippfile(klippfile)
29
+ yaml = YAML.load(File.read(klippfile))
30
+ raise 'Tokens not matching' unless yaml.keys == @tokens.keys
31
+ yaml.each { |name, value| self[name] = value }
32
+ end
33
+
34
+ def klippfile
35
+ "#{@name}.klippfile"
36
+ end
37
+
38
+ def generated_klippfile
39
+ @tokens.map { |name, t| "#{name}:\n# #{t.subtitle}" }.join("\n\n")
40
+ end
41
+
42
+ def replace_tokens(string_with_tokens, delimiter='XX')
43
+ replaced = string_with_tokens
44
+ @tokens.each { |name, t| replaced.gsub!(delimiter+name+delimiter, t.value ? t.value.to_s : '') }
45
+ replaced
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,35 @@
1
+ require 'yaml'
2
+
3
+ module Klipp
4
+
5
+ class Token
6
+ attr_accessor :value
7
+ attr_reader :title, :subtitle, :default, :validate, :not_valid_response
8
+
9
+ def initialize(token_yml)
10
+ if token_yml.is_a? String
11
+ parsed = YAML.load token_yml
12
+ else
13
+ parsed = token_yml
14
+ end
15
+
16
+ @title = parsed['title']
17
+ @subtitle = parsed['subtitle']
18
+ @default = parsed['default']
19
+ @validate = parsed['validate']
20
+ @not_valid_response = parsed['not_valid_response']
21
+
22
+ rescue Exception => e
23
+ p token_yml
24
+ end
25
+
26
+ def ask_for_input(terminal = nil)
27
+ @value = (terminal || HighLine.new).ask("\n<%= color('#{@title}', BOLD) %> #{"(#{@subtitle})" if @subtitle})?") do |q|
28
+ q.default = @default if @default
29
+ q.validate = @validate if @validate
30
+ q.responses[:not_valid] = @not_valid_response if @not_valid_response
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Klipp
2
+ VERSION = "0.0.1"
3
+ end
data/lib/klipp.rb ADDED
@@ -0,0 +1,135 @@
1
+ require 'klipp/buffered_output'
2
+ require 'klipp/version'
3
+ require 'klipp/configuration'
4
+ require 'klipp/token'
5
+ require 'klipp/template'
6
+ require 'klipp/parameter_list'
7
+ require 'klipp/project'
8
+ require 'colorize'
9
+
10
+ module Klipp
11
+ extend BufferedOutput::ClassMethods
12
+
13
+ def self.display_exception exception
14
+ if exception.is_a? HelpRequest
15
+ help = exception
16
+ else
17
+ help = HelpRequest.new exception.message, true
18
+ help.set_backtrace(exception.backtrace)
19
+ end
20
+
21
+ buffer_puts help.message
22
+ exit help.exit_status
23
+ end
24
+
25
+ def self.route(*argv)
26
+ @params = Klipp::ParameterList.new(argv)
27
+ command = @params.shift_argument
28
+ case command
29
+ when 'version'
30
+ version
31
+ when 'list'
32
+ list
33
+ when 'prepare'
34
+ prepare @params.first
35
+ when 'create'
36
+ create @params.first
37
+ when nil
38
+ raise HelpRequest.new('Use one of the commands below to start with klipp.', false, true)
39
+ else
40
+ raise "Unknown command: #{command}"
41
+ end
42
+
43
+ rescue Exception => e
44
+ display_exception e
45
+ end
46
+
47
+ def self.version
48
+ buffer_puts Klipp::VERSION
49
+ end
50
+
51
+ def self.list
52
+ files = template_files
53
+
54
+ raise "No templates found. Create a template directory and .yml file in #{Klipp::Configuration.templates_dir}" unless files.length > 0
55
+
56
+ buffer_puts("Available templates for use with #{'klipp prepare'.yellow} or #{'klipp create'.yellow}:\n\n")
57
+ files.each do |file|
58
+ buffer_puts(" * #{File.basename(file, '.*').green}")
59
+ end
60
+ end
61
+
62
+ def self.prepare(template_name)
63
+ raise HelpRequest.new 'Add a template name to the `prepare` command.' unless template_name
64
+
65
+ template = Klipp::Template.new(Klipp::Configuration.templates_dir, template_name)
66
+ raise "#{template.klippfile} already exists. Delete it if you want to prepare a new template." if File.exists? template.klippfile
67
+ IO.write(template.klippfile, template.generated_klippfile)
68
+ end
69
+
70
+ def self.create(template_name)
71
+ if template_name
72
+ klippfile = File.join(Dir.pwd, "#{template_name}.klippfile")
73
+ else
74
+ klippfile = Dir.glob(File.join(Dir.pwd, '*.klippfile')).first
75
+ template_name = File.basename(klippfile, File.extname(klippfile)) if klippfile
76
+ end
77
+
78
+ template = Klipp::Template.new(Klipp::Configuration.templates_dir, template_name)
79
+
80
+ if klippfile
81
+ # load token values from klippfile
82
+ template.load_klippfile klippfile
83
+ elsif template_name
84
+ # ask for token values with highline
85
+ raise "Direct user input not yet supported. Use #{'klipp prepare'.yellow} to prepare a .klippfile"
86
+ else
87
+ raise "Add a template name to the `create` command, or use #{'klipp prepare'.yellow} to prepare a .klippfile"
88
+ end
89
+
90
+ project = Klipp::Project.new(template)
91
+ project.create
92
+ end
93
+
94
+ private
95
+
96
+ def self.template_files
97
+ Dir.glob File.join(Klipp::Configuration.templates_dir, '*.yml')
98
+ end
99
+
100
+ end
101
+
102
+ class HelpRequest < StandardError
103
+ def initialize(msg, unknown=false, show_title=false)
104
+ @unknown = unknown
105
+ @show_title = show_title
106
+ super(msg)
107
+ end
108
+
109
+ def message
110
+ if @unknown
111
+ "[!] #{super.to_s}".red+"\n\n#{commands}\n\n#{self.backtrace.join("\n")}"
112
+ else
113
+ "#{@show_title ? title+"\n\n" : ''}"+"[?] #{super.to_s}".yellow+"\n\n#{commands}"
114
+ end
115
+ end
116
+
117
+ def title
118
+ "\033[1mKlipp\033[22m, Xcode templates for the rest of us. Version: #{Klipp::VERSION}"
119
+ end
120
+
121
+ def commands
122
+ commands = [
123
+ version: 'Display the Klipp version number.',
124
+ list: "List all available klipp templates in #{Klipp::Configuration.templates_dir}",
125
+ prepare: 'Prepare a .klippfile to edit in your favorite text editor.',
126
+ create: 'Create a project based on the template name or .klippfile in the current directory'
127
+ ]
128
+ command_list = commands.map { |cmd| cmd.map { |key, summary| " * klipp #{key.to_s.ljust(10).green} #{summary}" } }.join("\n")
129
+ "Commands:\n\n#{command_list}"
130
+ end
131
+
132
+ def exit_status
133
+ @unknown ? 2 : 1
134
+ end
135
+ end
@@ -0,0 +1,4 @@
1
+ PARTNER: The Prestigeous Partner
2
+ PROJECT_TITLE: Project X
3
+ PROJECT_ID: ProjectX
4
+ CLASS_PREFIX: PJX
@@ -0,0 +1,5 @@
1
+ PARTNER: The Prestigeous Partner
2
+ PROJECT_TITLE: Project X
3
+ PROJECT_ID: ProjectX
4
+ CLASS_PREFIX: PJX
5
+ EXTRA_TOKEN: Bad Token
@@ -0,0 +1,11 @@
1
+ PARTNER:
2
+ # e.g. 'FC Utrecht'
3
+
4
+ PROJECT_TITLE:
5
+ # e.g. 'FC Utrecht Agenda'
6
+
7
+ PROJECT_ID:
8
+ # e.g. 'FCUtrechtAgenda'
9
+
10
+ CLASS_PREFIX:
11
+ # e.g. 'FCU'
@@ -0,0 +1,3 @@
1
+ PARTNER: The Prestigeous Partner
2
+ PROJECT_TITLE: Project X
3
+ PROJECT_ID: ProjectX
@@ -0,0 +1,4 @@
1
+ PARTNERSSSSSS: The Prestigeous Partner
2
+ PROJECT_TITLE: Project X
3
+ PROJECT_ID: ProjectX
4
+ CLASS_PREFIX: PJX
@@ -0,0 +1,5 @@
1
+ title: Partner name
2
+ subtitle: e.g. 'FC Utrecht'
3
+ default: Qwerty
4
+ validate: !ruby/regexp '/^[A-Z][A-Za-z0-9 ]{2,}$/'
5
+ not_valid_response: Should be at least three characters long and start with a capital character
@@ -0,0 +1,3 @@
1
+ Regular content stays untouched
2
+ Tokens are replaced: XXPROJECT_IDXX
3
+ Even if it's in the middle of something else BlablablaXXPROJECT_IDXXBlabla
@@ -0,0 +1,3 @@
1
+ Regular content stays untouched
2
+ Tokens are replaced: XXPROJECT_IDXX
3
+ Even if it's in the middle of something else BlablablaXXPROJECT_IDXXBlabla
@@ -0,0 +1,3 @@
1
+ Regular content stays untouched
2
+ Tokens are replaced: XXPROJECT_IDXX
3
+ Even if it's in the middle of something else BlablablaXXPROJECT_IDXXBlabla
@@ -0,0 +1,29 @@
1
+ PARTNER:
2
+ title: Partner name
3
+ subtitle: e.g. 'FC Utrecht'
4
+ default: Qwerty
5
+ validate: !ruby/regexp '/^[A-Z][A-Za-z0-9 ]{2,}$/'
6
+ not_valid_response: Should be at least three characters long and start with a capital character
7
+
8
+ PROJECT_TITLE:
9
+ title: Project title
10
+ subtitle: e.g. 'FC Utrecht Agenda'
11
+ default: #PARTNER# Agenda
12
+ validate: !ruby/regexp '/^[A-Z][A-Za-z0-9 ]{2,}$/'
13
+ not_valid_response: Should be at least three characters long and start with a capital character
14
+
15
+ PROJECT_ID:
16
+ title: Project id
17
+ subtitle: e.g. 'FCUtrechtAgenda'
18
+ default: #PROJECT_ID#
19
+ whitespace: :strip_and_collapse
20
+ validate: !ruby/regexp '/^[A-Z][A-Za-z0-9 ]{2,}$/'
21
+ not_valid_response: Should be at least three characters long and start with a capital character
22
+
23
+ CLASS_PREFIX:
24
+ title: Class prefix
25
+ subtitle: e.g. 'FCU'
26
+ default: #PROJECT_ID#
27
+ whitespace: :strip_and_collapse
28
+ validate: !ruby/regexp '/^[A-Z][A-Za-z0-9 ]{2,}$/'
29
+ not_valid_response: Should be at least three characters long and start with a capital character
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+
4
+ describe Klipp::Configuration do
5
+
6
+ it 'points to .klipp inside the user\'s home directory' do
7
+ Klipp::Configuration.root_dir.should eq File.join(Dir.home, '.klipp')
8
+ end
9
+
10
+ context 'with auto create enabled' do
11
+
12
+ before do
13
+ Klipp::Configuration.auto_create_dirs = true
14
+ end
15
+
16
+ it 'auto creates the .klipp directory' do
17
+ Dir.expects(:mkdir).once
18
+ File.expects(:exists?).once.returns false
19
+ Klipp::Configuration.root_dir
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+
4
+ describe Klipp::Project do
5
+
6
+ context 'with a template' do
7
+
8
+ before do
9
+ fixtures_dir = File.join(File.dirname(__dir__), 'fixtures')
10
+ Klipp::Configuration.stubs(:root_dir).returns fixtures_dir
11
+ @template = Klipp::Template.new(Klipp::Configuration.templates_dir, 'Example')
12
+ @template.load_klippfile File.join(fixtures_dir, 'klipps', 'Example.klippfile')
13
+ @project = Klipp::Project.new(@template)
14
+ Dir.stubs(:pwd).returns(Dir.mktmpdir)
15
+ end
16
+
17
+ it 'copies files while replacing paths' do
18
+ @project.create
19
+ File.exists?(File.join Dir.pwd, 'Example', 'ProjectX').should be true
20
+ File.exists?(File.join Dir.pwd, 'Example', 'PJXPrefixedFile.txt').should be true
21
+ File.exists?(File.join Dir.pwd, 'Example', 'ProjectX', 'BinaryFile.png').should be true
22
+ File.exists?(File.join Dir.pwd, 'Example', 'ProjectX', 'PJXPrefixedFileInDirectory.txt').should be true
23
+ end
24
+
25
+ it 'replaces file contens while transferring' do
26
+ @project.create
27
+ File.read(File.join Dir.pwd, 'Example', 'PJXPrefixedFile.txt').should include 'Regular content stays untouched'
28
+ File.read(File.join Dir.pwd, 'Example', 'PJXPrefixedFile.txt').should include 'Tokens are replaced: ProjectX'
29
+ File.read(File.join Dir.pwd, 'Example', 'PJXPrefixedFile.txt').should include 'Even if it\'s in the middle of something else BlablablaProjectXBlabla'
30
+ end
31
+
32
+ it 'copies binary files without replacing contents' do
33
+ @project.create
34
+ template_png = File.join(Klipp::Configuration.templates_dir, 'Example', 'XXPROJECT_IDXX', 'BinaryFile.png')
35
+ transferred_png = File.join(Dir.pwd, 'Example', 'ProjectX', 'BinaryFile.png')
36
+ FileUtils.compare_file(template_png, transferred_png).should be true
37
+ end
38
+
39
+ it 'halts transfer if the directory already exists' do
40
+ FileUtils.mkdir_p File.join(Dir.pwd, 'Example')
41
+ expect { @project.create }.to raise_error RuntimeError
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Klipp::Template do
4
+
5
+ it 'raises an error when initialized with an invalid path or name' do
6
+ expect {
7
+ Klipp::Template.new('bull', 'shit')
8
+ }.to raise_error(RuntimeError)
9
+ end
10
+
11
+ context 'with a valid path and name' do
12
+
13
+ before do
14
+ @templates_dir = File.join(__dir__, '..', 'fixtures', 'templates')
15
+ @klipps_dir = File.join(__dir__, '..', 'fixtures', 'klipps')
16
+ end
17
+
18
+ it 'initializes' do
19
+ Klipp::Template.new(@templates_dir, 'Example').should be_a Klipp::Template
20
+ end
21
+
22
+ xit 'validates the template yml' do
23
+ true.should eq false
24
+ end
25
+
26
+ context 'with a template object' do
27
+
28
+ before do
29
+ @template = Klipp::Template.new(@templates_dir, 'Example')
30
+ end
31
+
32
+ it 'knows the name of the corresponding .klippfile' do
33
+ @template.klippfile.should eq 'Example.klippfile'
34
+ end
35
+
36
+ it 'can generate stubbed contents for a .klippfile' do
37
+ @template.generated_klippfile.should eq File.read(File.join(@klipps_dir,'Generated.klippfile'))
38
+ end
39
+
40
+ it 'contains a matching number of tokens' do
41
+ @template.tokens.should have_exactly(4).items
42
+ end
43
+
44
+ it 'loads the token values from a valid Klippfile' do
45
+ @template.load_klippfile File.join(@klipps_dir, 'Example.klippfile')
46
+ @template['PARTNER'].should eq 'The Prestigeous Partner'
47
+ end
48
+
49
+ it 'raises an error when the Klippfile is malformed' do
50
+ expect { @template.load_klippfile File.join(@klipps_dir, 'MalformedExample.klippfile') }.to raise_error RuntimeError
51
+ end
52
+
53
+ it 'raises an error when the Klippfile contains excess tokens' do
54
+ expect { @template.load_klippfile File.join(@klipps_dir, 'ExcessiveExample.klippfile') }.to raise_error RuntimeError
55
+ end
56
+
57
+ it 'raises an error when the Klippfile contains too few tokens' do
58
+ expect { @template.load_klippfile File.join(@klipps_dir, 'LackingExample.klippfile') }.to raise_error RuntimeError
59
+ end
60
+
61
+ end
62
+
63
+ context 'with a template object and token values' do
64
+
65
+ before do
66
+ @template = Klipp::Template.new(@templates_dir, 'Example')
67
+ @template['CLASS_PREFIX'] = 'PJX'
68
+ @template['PROJECT_ID'] = 'ProjectX'
69
+ @template['PROJECT_TITLE'] = 'Project X'
70
+ end
71
+
72
+ it 'replaces delimited tokens in a string' do
73
+ @template.replace_tokens('XXCLASS_PREFIXXX XXPROJECT_IDXX XXPROJECT_TITLEXX').should eq 'PJX ProjectX Project X'
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+ require 'highline/import'
3
+
4
+ describe Klipp::Token do
5
+
6
+ context 'with a single token yaml' do
7
+
8
+ before do
9
+ @token = Klipp::Token.new read_fixture('klipps/single-token.yml')
10
+ end
11
+
12
+ it 'has a title' do
13
+ @token.title.should eq 'Partner name'
14
+ end
15
+
16
+ it 'has a subtitle' do
17
+ @token.subtitle.should eq "e.g. 'FC Utrecht'"
18
+ end
19
+
20
+ it 'has a default value' do
21
+ @token.default.should eq 'Qwerty'
22
+ end
23
+
24
+ it 'has a validate regex' do
25
+ @token.validate.should eq /^[A-Z][A-Za-z0-9 ]{2,}$/
26
+ end
27
+
28
+ it 'has a response when not valid' do
29
+ @token.not_valid_response.should eq 'Should be at least three characters long and start with a capital character'
30
+ end
31
+
32
+ end
33
+
34
+ context 'with a valid token object' do
35
+
36
+ before do
37
+ @input = StringIO.new
38
+ @output = StringIO.new
39
+ @terminal = HighLine.new(@input, @output)
40
+ @token = Klipp::Token.new read_fixture('klipps/single-token.yml')
41
+ end
42
+
43
+ it 'can ask a question' do
44
+ simulate_input 'Something'
45
+
46
+ @token.ask_for_input @terminal
47
+
48
+ @output.string.should include 'Partner name'
49
+ @output.string.should include "e.g. 'FC Utrecht'"
50
+ end
51
+
52
+ it 'records the answer' do
53
+ name = 'Qwerty'
54
+ simulate_input name
55
+
56
+ @token.ask_for_input @terminal
57
+
58
+ @token.value.should eq name
59
+ end
60
+
61
+ it 'uses highline validation' do
62
+ simulate_input 'Qw'
63
+
64
+ expect {
65
+ @token.ask_for_input @terminal
66
+ }.to raise_error(EOFError)
67
+
68
+ @output.string.should include 'Should be at least three characters long and start with a capital character'
69
+ end
70
+
71
+ it 'prefills a default value' do
72
+ simulate_input
73
+
74
+ @token.ask_for_input @terminal
75
+
76
+ @token.value.should eq 'Qwerty'
77
+ end
78
+
79
+ def simulate_input(value = '')
80
+ @input << value << "\n"
81
+ @input.rewind
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,139 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Klipp do
5
+
6
+ before do
7
+ @output = Klipp.output = StringIO.new
8
+ end
9
+
10
+ describe 'when routing' do
11
+
12
+ it 'prints help without any parameters' do
13
+ expect { Klipp.route *%w[] }.to raise_error SystemExit
14
+ should include 'Xcode templates for the rest of us.'
15
+ end
16
+
17
+ it 'prints warnings accordingly' do
18
+ expect { Klipp.route *%w[prepare Bullshit] }.to raise_error SystemExit
19
+ should include '[!]'
20
+ end
21
+
22
+ it 'raises a warning when routing unknown commands' do
23
+ expect { Klipp.route *%w[allyourbase] }.to raise_error SystemExit
24
+ should include '[!]'
25
+ end
26
+
27
+ it 'routes `prepare`' do
28
+ Klipp.expects(:prepare)
29
+ Klipp.route *%w[prepare Example]
30
+ end
31
+
32
+ it 'prints help when preparing without a template name' do
33
+ expect { Klipp.route *%w[prepare] }.to raise_error SystemExit
34
+ should include 'Add a template name to the `prepare` command.'
35
+ end
36
+
37
+ it 'routes `list`' do
38
+ Klipp.expects(:list)
39
+ Klipp.route *%w[list]
40
+ end
41
+
42
+ it 'routes `version`' do
43
+ Klipp.expects(:version)
44
+ Klipp.route *%w[version]
45
+ end
46
+
47
+ it 'routes `create``' do
48
+ Klipp.expects(:create)
49
+ Klipp.route *%w[create]
50
+ end
51
+
52
+ end
53
+
54
+ describe 'when preparing' do
55
+
56
+ before do
57
+ Klipp::Configuration.stubs(:root_dir).returns File.join(__dir__, 'fixtures')
58
+ end
59
+
60
+ it 'raises help if the template argument is missing' do
61
+ expect { Klipp.prepare nil }.to raise_error HelpRequest
62
+ end
63
+
64
+ it 'saves a klippfile' do
65
+ IO.expects(:write).with('Example.klippfile', anything).once
66
+ Klipp.prepare 'Example'
67
+ end
68
+
69
+ it 'raises an error when the template argument is incorrect' do
70
+ expect { Klipp.prepare 'Bullshit' }.to raise_error RuntimeError
71
+ end
72
+
73
+ it 'raises an error when a .klippfile for the template already exists' do
74
+ File.stubs(:exists?).with(anything).returns true
75
+ expect { Klipp.prepare 'Example' }.to raise_error RuntimeError
76
+ end
77
+
78
+ end
79
+
80
+ describe 'when listing' do
81
+
82
+ it 'lists templates' do
83
+ Klipp::Configuration.stubs(:root_dir).returns File.join(__dir__, 'fixtures')
84
+ Klipp.list
85
+ should include 'Example'
86
+ end
87
+
88
+ it 'raises an error when there are no templates' do
89
+ Klipp.expects(:template_files).returns([])
90
+ expect { Klipp.list }.to raise_error RuntimeError
91
+ end
92
+
93
+ end
94
+
95
+ describe 'when versioning' do
96
+
97
+ it 'displays the current version' do
98
+ Klipp.version
99
+ should include Klipp::VERSION
100
+ end
101
+
102
+ end
103
+
104
+ describe 'when creating' do
105
+
106
+ before do
107
+ Klipp::Configuration.stubs(:root_dir).returns File.join(__dir__, 'fixtures')
108
+ @example_klippfile_contents = File.read(File.join(__dir__, 'fixtures', 'klipps', 'Example.klippfile'))
109
+ end
110
+
111
+ it 'calls create on a project with a named template' do
112
+ mock_project = mock
113
+ mock_project.expects(:create)
114
+ Klipp::Project.expects(:new).with(is_a(Klipp::Template)).returns(mock_project)
115
+ File.stubs(:read).with(anything).returns( @example_klippfile_contents )
116
+ Klipp.create('Example')
117
+ end
118
+
119
+ it 'calls create on a project with an inferred template' do
120
+ Dir.expects(:glob).with(anything, anything).returns %w(Example.klippfile)
121
+ mock_project = mock
122
+ mock_project.expects(:create)
123
+ Klipp::Project.expects(:new).with(is_a(Klipp::Template)).returns(mock_project)
124
+ File.stubs(:read).with(anything).returns( @example_klippfile_contents )
125
+ Klipp.create nil
126
+ end
127
+
128
+ it 'raises an error when the template name can\'t be inferred' do
129
+ Dir.expects(:glob).with(anything, anything).returns []
130
+ expect { Klipp.create nil }.to raise_error RuntimeError
131
+ end
132
+
133
+ end
134
+
135
+ def subject
136
+ @output.string
137
+ end
138
+
139
+ end
@@ -0,0 +1,17 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+ require 'mocha/api'
4
+
5
+ SimpleCov.start
6
+ SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
7
+
8
+ require 'klipp'
9
+
10
+ def read_fixture fixture_name
11
+ File.read(File.join(__dir__, 'fixtures', fixture_name))
12
+ end
13
+
14
+ RSpec.configure do |config|
15
+ config.mock_framework = :mocha
16
+ config.order = 'random'
17
+ end
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: klipp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Eric-Paul Lecluse
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: highline
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ptools
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: colored
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Klipp, Xcode templates for the rest of us.
84
+ email:
85
+ - e@epologee.com
86
+ executables:
87
+ - klipp
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/klipp
97
+ - build_klipp.sh
98
+ - klipp.gemspec
99
+ - lib/klipp.rb
100
+ - lib/klipp/buffered_output.rb
101
+ - lib/klipp/configuration.rb
102
+ - lib/klipp/parameter_list.rb
103
+ - lib/klipp/project.rb
104
+ - lib/klipp/template.rb
105
+ - lib/klipp/token.rb
106
+ - lib/klipp/version.rb
107
+ - spec/fixtures/klipps/Example.klippfile
108
+ - spec/fixtures/klipps/ExcessiveExample.klippfile
109
+ - spec/fixtures/klipps/Generated.klippfile
110
+ - spec/fixtures/klipps/LackingExample.klippfile
111
+ - spec/fixtures/klipps/MalformedExample.klippfile
112
+ - spec/fixtures/klipps/single-token.yml
113
+ - spec/fixtures/templates/Example.yml
114
+ - spec/fixtures/templates/Example/RegularFileWithContents.txt
115
+ - spec/fixtures/templates/Example/XXCLASS_PREFIXXXPrefixedFile.txt
116
+ - spec/fixtures/templates/Example/XXPROJECT_IDXX/BinaryFile.png
117
+ - spec/fixtures/templates/Example/XXPROJECT_IDXX/XXCLASS_PREFIXXXPrefixedFileInDirectory.txt
118
+ - spec/klipp/configuration_spec.rb
119
+ - spec/klipp/project_spec.rb
120
+ - spec/klipp/template_spec.rb
121
+ - spec/klipp/token_spec.rb
122
+ - spec/klipp_spec.rb
123
+ - spec/spec_helper.rb
124
+ homepage: https://github.com/epologee/klipp
125
+ licenses:
126
+ - MIT
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubyforge_project:
144
+ rubygems_version: 2.0.2
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: Klipp is a command line gem for creating new (Xcode) projects from existing
148
+ templates. Unlike Apple's private plist-based templating system, Klipp takes an
149
+ existing Xcode project and creates a new project by copying and modifying an existing
150
+ template project by your own specifications.
151
+ test_files:
152
+ - spec/fixtures/klipps/Example.klippfile
153
+ - spec/fixtures/klipps/ExcessiveExample.klippfile
154
+ - spec/fixtures/klipps/Generated.klippfile
155
+ - spec/fixtures/klipps/LackingExample.klippfile
156
+ - spec/fixtures/klipps/MalformedExample.klippfile
157
+ - spec/fixtures/klipps/single-token.yml
158
+ - spec/fixtures/templates/Example.yml
159
+ - spec/fixtures/templates/Example/RegularFileWithContents.txt
160
+ - spec/fixtures/templates/Example/XXCLASS_PREFIXXXPrefixedFile.txt
161
+ - spec/fixtures/templates/Example/XXPROJECT_IDXX/BinaryFile.png
162
+ - spec/fixtures/templates/Example/XXPROJECT_IDXX/XXCLASS_PREFIXXXPrefixedFileInDirectory.txt
163
+ - spec/klipp/configuration_spec.rb
164
+ - spec/klipp/project_spec.rb
165
+ - spec/klipp/template_spec.rb
166
+ - spec/klipp/token_spec.rb
167
+ - spec/klipp_spec.rb
168
+ - spec/spec_helper.rb