monkey_butler 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.bundle/config +2 -0
- data/.gitignore +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +122 -0
- data/Guardfile +8 -0
- data/LICENSE +202 -0
- data/README.md +221 -0
- data/Rakefile +16 -0
- data/bin/mb +6 -0
- data/lib/monkey_butler.rb +1 -0
- data/lib/monkey_butler/actions.rb +38 -0
- data/lib/monkey_butler/cli.rb +354 -0
- data/lib/monkey_butler/databases/abstract_database.rb +49 -0
- data/lib/monkey_butler/databases/cassandra_database.rb +69 -0
- data/lib/monkey_butler/databases/sqlite_database.rb +105 -0
- data/lib/monkey_butler/migrations.rb +52 -0
- data/lib/monkey_butler/project.rb +90 -0
- data/lib/monkey_butler/targets/base.rb +85 -0
- data/lib/monkey_butler/targets/cassandra/cassandra_target.rb +72 -0
- data/lib/monkey_butler/targets/cassandra/create_schema_migrations.cql.erb +16 -0
- data/lib/monkey_butler/targets/cassandra/migration.cql.erb +0 -0
- data/lib/monkey_butler/targets/cocoapods/cocoapods_target.rb +43 -0
- data/lib/monkey_butler/targets/cocoapods/podspec.erb +12 -0
- data/lib/monkey_butler/targets/maven/maven_target.rb +60 -0
- data/lib/monkey_butler/targets/maven/project/.gitignore +6 -0
- data/lib/monkey_butler/targets/maven/project/MonkeyButler.iml +19 -0
- data/lib/monkey_butler/targets/maven/project/build.gradle +54 -0
- data/lib/monkey_butler/targets/sqlite/create_monkey_butler_tables.sql.erb +15 -0
- data/lib/monkey_butler/targets/sqlite/migration.sql.erb +11 -0
- data/lib/monkey_butler/targets/sqlite/sqlite_target.rb +91 -0
- data/lib/monkey_butler/templates/Gemfile.erb +4 -0
- data/lib/monkey_butler/templates/gitignore.erb +1 -0
- data/lib/monkey_butler/util.rb +71 -0
- data/lib/monkey_butler/version.rb +3 -0
- data/logo.jpg +0 -0
- data/monkey_butler.gemspec +33 -0
- data/spec/cli_spec.rb +700 -0
- data/spec/databases/cassandra_database_spec.rb +241 -0
- data/spec/databases/sqlite_database_spec.rb +181 -0
- data/spec/migrations_spec.rb +4 -0
- data/spec/project_spec.rb +128 -0
- data/spec/sandbox/cassandra/.gitignore +2 -0
- data/spec/sandbox/cassandra/.monkey_butler.yml +7 -0
- data/spec/sandbox/cassandra/migrations/20140523123443021_create_sandbox.cql.sql +14 -0
- data/spec/sandbox/cassandra/sandbox.cql +0 -0
- data/spec/sandbox/sqlite/.gitignore +2 -0
- data/spec/sandbox/sqlite/.monkey_butler.yml +7 -0
- data/spec/sandbox/sqlite/migrations/20140523123443021_create_sandbox.sql +14 -0
- data/spec/sandbox/sqlite/sandbox.sql +0 -0
- data/spec/spec_helper.rb +103 -0
- data/spec/targets/cassandra_target_spec.rb +191 -0
- data/spec/targets/cocoapods_target_spec.rb +197 -0
- data/spec/targets/maven_target_spec.rb +156 -0
- data/spec/targets/sqlite_target_spec.rb +103 -0
- data/spec/util_spec.rb +13 -0
- metadata +260 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
/* Create Monkey Butler Tables */
|
2
|
+
|
3
|
+
-- Maintains list of applied migrations
|
4
|
+
USE KEYSPACE <%= keyspace -%>;
|
5
|
+
CREATE TABLE schema_migrations(
|
6
|
+
version INTEGER UNIQUE NOT NULL
|
7
|
+
);
|
8
|
+
|
9
|
+
/* Create application tables */
|
10
|
+
|
11
|
+
/*
|
12
|
+
CREATE TABLE table_name (
|
13
|
+
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
14
|
+
...
|
15
|
+
);
|
16
|
+
*/
|
File without changes
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'monkey_butler/targets/base'
|
2
|
+
|
3
|
+
module MonkeyButler
|
4
|
+
module Targets
|
5
|
+
class CocoapodsTarget < MonkeyButler::Targets::Base
|
6
|
+
def init
|
7
|
+
unless project.config['cocoapods.repo']
|
8
|
+
project.config['cocoapods.repo'] = ask("What is the name of your Cocoapods specs repo? ")
|
9
|
+
end
|
10
|
+
if options['bundler']
|
11
|
+
append_to_file 'Gemfile', "gem 'cocoapods'\n"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate
|
16
|
+
invoke :validate
|
17
|
+
template('podspec.erb', podspec_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate
|
21
|
+
fail Error, "Invalid configuration: cocoapods.repo is not configured." unless cocoapods_repo
|
22
|
+
end
|
23
|
+
|
24
|
+
def push
|
25
|
+
invoke :validate
|
26
|
+
run "pod repo push #{options['quiet'] && '--silent '}--allow-warnings #{cocoapods_repo} #{podspec_name}"
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def cocoapods_repo
|
31
|
+
project.config['cocoapods.repo']
|
32
|
+
end
|
33
|
+
|
34
|
+
def podspec_name
|
35
|
+
"#{project.name}.podspec"
|
36
|
+
end
|
37
|
+
|
38
|
+
def pod_version
|
39
|
+
unique_tag_for_version(migrations.latest_version)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Pod::Spec.new do |s|
|
2
|
+
s.name = "<%= project.name %>"
|
3
|
+
s.version = "<%= pod_version %>"
|
4
|
+
s.summary = "Packages the database schema and migrations for <%= project.name -%>"
|
5
|
+
s.homepage = "<%= project.config['homepage'] || "http://github.com/layerhq" %>"
|
6
|
+
s.author = { "<%= project.git_user_name -%>" => "<%= project.git_user_email -%>" }
|
7
|
+
s.source = { :git => "<%= project.git_url -%>", :tag => "<%= pod_version -%>" }
|
8
|
+
s.license = 'Commercial'
|
9
|
+
s.requires_arc = true
|
10
|
+
|
11
|
+
s.resource_bundles = { '<%= project.name -%>' => ['migrations/*', '<%= project.schema_path -%>'] }
|
12
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'monkey_butler/targets/base'
|
2
|
+
|
3
|
+
module MonkeyButler
|
4
|
+
module Targets
|
5
|
+
class MavenTarget < MonkeyButler::Targets::Base
|
6
|
+
def init
|
7
|
+
unless project.config['maven.url']
|
8
|
+
project.config['maven.url'] = ask("What is the URL of your Java Maven repo? ")
|
9
|
+
end
|
10
|
+
unless project.config['maven.username']
|
11
|
+
project.config['maven.username'] = ask("What is the username for your Java Maven repo? ")
|
12
|
+
end
|
13
|
+
unless project.config['maven.password']
|
14
|
+
project.config['maven.password'] = ask("What is the password for your Java Maven repo? ")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate
|
19
|
+
invoke :validate
|
20
|
+
remove_file "project"
|
21
|
+
empty_directory "project"
|
22
|
+
empty_directory "project/src"
|
23
|
+
empty_directory "project/src/main"
|
24
|
+
empty_directory "project/src/main/resources"
|
25
|
+
empty_directory "project/src/main/resources/schema"
|
26
|
+
|
27
|
+
copy_file "project/build.gradle", "project/build.gradle"
|
28
|
+
FileUtils.cp_r project.schema_path, "project/src/main/resources/schema/schema.sql"
|
29
|
+
FileUtils.cp_r project.migrations_path, "project/src/main/resources"
|
30
|
+
|
31
|
+
run "cd project && gradle#{options['quiet'] && ' -q '} -Pversion=#{migrations.latest_version} clean jar"
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate
|
35
|
+
fail Error, "Invalid configuration: maven.repo is not configured." unless maven_url
|
36
|
+
fail Error, "Invalid configuration: maven.username is not configured." unless maven_username
|
37
|
+
fail Error, "Invalid configuration: maven.password is not configured." unless maven_password
|
38
|
+
end
|
39
|
+
|
40
|
+
def push
|
41
|
+
invoke :validate
|
42
|
+
run "cd project && gradle#{options['quiet'] && ' -q'} -Pversion=#{migrations.latest_version} -Purl=#{maven_url} -Pusername=#{maven_username} -Ppassword=#{maven_password} publish"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def maven_url
|
48
|
+
project.config['maven.url']
|
49
|
+
end
|
50
|
+
|
51
|
+
def maven_username
|
52
|
+
project.config['maven.username']
|
53
|
+
end
|
54
|
+
|
55
|
+
def maven_password
|
56
|
+
project.config['maven.password']
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.1.1" type="JAVA_MODULE" version="4">
|
3
|
+
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
4
|
+
<output url="file://$MODULE_DIR$/build/classes/main" />
|
5
|
+
<output-test url="file://$MODULE_DIR$/build/classes/test" />
|
6
|
+
<exclude-output />
|
7
|
+
<content url="file://$MODULE_DIR$">
|
8
|
+
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
9
|
+
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
10
|
+
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
11
|
+
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
12
|
+
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
13
|
+
<excludeFolder url="file://$MODULE_DIR$/build" />
|
14
|
+
</content>
|
15
|
+
<orderEntry type="inheritedJdk" />
|
16
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
17
|
+
</component>
|
18
|
+
</module>
|
19
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
apply plugin: 'java'
|
2
|
+
apply plugin: 'maven'
|
3
|
+
apply plugin: 'maven-publish'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Specify:
|
7
|
+
* -Pversion=[VERSION]
|
8
|
+
* -Purl=[MAVEN URL TO PUBLISH TO] e.g. "http://nexus.dev.layer.com:8081/nexus/content/repositories/releases" or "${System.env.HOME}/.m2/repository"
|
9
|
+
* -Pusername=[USERNAME FOR MAVEN PUBLISH]
|
10
|
+
* -Ppassword=[PASSWORD FOR MAVEN PUBLISH]
|
11
|
+
*/
|
12
|
+
|
13
|
+
ext {
|
14
|
+
mavenUrl = rootProject.hasProperty("url") ? rootProject.property("url") : "${System.env.HOME}/.m2/repository"
|
15
|
+
mavenUsername = rootProject.hasProperty("username") ? rootProject.property("username") : null;
|
16
|
+
mavenPassword = rootProject.hasProperty("password") ? rootProject.property("password") : null;
|
17
|
+
}
|
18
|
+
|
19
|
+
compileJava {
|
20
|
+
sourceCompatibility = 1.6
|
21
|
+
targetCompatibility = 1.6
|
22
|
+
}
|
23
|
+
|
24
|
+
repositories {
|
25
|
+
mavenCentral()
|
26
|
+
}
|
27
|
+
|
28
|
+
/*************************************************
|
29
|
+
* Uploading
|
30
|
+
************************************************/
|
31
|
+
task Jar(type: Jar) {
|
32
|
+
jar.archiveName = "monkeybutler-" + version + ".jar"
|
33
|
+
}
|
34
|
+
|
35
|
+
publishing {
|
36
|
+
publications {
|
37
|
+
mavenJava(MavenPublication) {
|
38
|
+
groupId "com.layer"
|
39
|
+
artifactId "monkeybutler"
|
40
|
+
from components.java
|
41
|
+
}
|
42
|
+
}
|
43
|
+
repositories {
|
44
|
+
maven {
|
45
|
+
credentials {
|
46
|
+
username = mavenUsername;
|
47
|
+
password = mavenPassword;
|
48
|
+
}
|
49
|
+
url = mavenUrl;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/* Create Monkey Butler Tables */
|
2
|
+
|
3
|
+
-- Maintains list of applied migrations
|
4
|
+
CREATE TABLE schema_migrations(
|
5
|
+
version INTEGER UNIQUE NOT NULL
|
6
|
+
);
|
7
|
+
|
8
|
+
/* Create application tables */
|
9
|
+
|
10
|
+
/*
|
11
|
+
CREATE TABLE table_name (
|
12
|
+
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
13
|
+
...
|
14
|
+
);
|
15
|
+
*/
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'monkey_butler/databases/sqlite_database'
|
2
|
+
|
3
|
+
module MonkeyButler
|
4
|
+
module Targets
|
5
|
+
class SqliteTarget < Base
|
6
|
+
# TODO: Need a way to do this for self elegantly...
|
7
|
+
def self.register_with_cli(cli)
|
8
|
+
cli.method_option :database, type: :string, aliases: '-d', desc: "Set target DATABASE", for: :dump
|
9
|
+
cli.method_option :database, type: :string, aliases: '-d', desc: "Set target DATABASE", for: :load
|
10
|
+
end
|
11
|
+
|
12
|
+
def init
|
13
|
+
migration_name = MonkeyButler::Util.migration_named('create_' + options['name'])
|
14
|
+
template('create_monkey_butler_tables.sql.erb', "migrations/#{migration_name}.sql")
|
15
|
+
git_add "migrations/#{migration_name}.sql"
|
16
|
+
create_file(database_path)
|
17
|
+
append_to_file '.gitignore', database_path
|
18
|
+
end
|
19
|
+
|
20
|
+
no_commands do
|
21
|
+
# NOTE: Used instead of database.url to avoid creation of the database in pretend mode
|
22
|
+
def database_path
|
23
|
+
database_url.path || database_url.opaque
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def new(name)
|
28
|
+
migration_ext = project.database_class.migration_ext
|
29
|
+
migration_name = MonkeyButler::Util.migration_named(name) + migration_ext
|
30
|
+
template('migration.sql.erb', "migrations/#{migration_name}")
|
31
|
+
git_add "migrations/#{migration_name}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def dump
|
35
|
+
database_url = (options[:database] && URI(options[:database])) || project.database_url
|
36
|
+
database_path = database_url.path || database_url.opaque
|
37
|
+
fail Error, "Cannot dump database: no file at path '#{database_path}'." unless File.exists?(database_path)
|
38
|
+
|
39
|
+
@database = MonkeyButler::Databases::SqliteDatabase.new(database_url)
|
40
|
+
fail Error, "Cannot dump database: the database at path '#{database_path}' does not have a `schema_migrations` table." unless database.migrations_table?
|
41
|
+
say "Dumping schema from database '#{database_path}'"
|
42
|
+
|
43
|
+
File.truncate(project.schema_path, 0)
|
44
|
+
|
45
|
+
types = { table: 'tables', index: 'indexes', trigger: 'triggers', view: 'views'}
|
46
|
+
types.each do |type, name|
|
47
|
+
say "Dumping #{name}..."
|
48
|
+
with_padding do
|
49
|
+
database.dump_to_schema(type, project.schema_path) do |name|
|
50
|
+
say "wrote #{type}: #{name}", :green
|
51
|
+
end
|
52
|
+
end
|
53
|
+
say
|
54
|
+
end
|
55
|
+
|
56
|
+
say "Dumping rows from 'schema_migrations'..."
|
57
|
+
with_padding do
|
58
|
+
File.open(project.schema_path, 'a') do |f|
|
59
|
+
database.all_versions.each do |version|
|
60
|
+
f.puts "INSERT INTO schema_migrations(version) VALUES (#{version});\n\n"
|
61
|
+
say "wrote version: #{version}", :green
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
say
|
66
|
+
|
67
|
+
say "Dump complete. Schema written to #{project.schema_path}."
|
68
|
+
end
|
69
|
+
|
70
|
+
def load
|
71
|
+
project = MonkeyButler::Project.load
|
72
|
+
unless File.size?(project.schema_path)
|
73
|
+
raise Error, "Cannot load database: empty schema found at #{project.schema_path}. Maybe you need to `mb migrate`?"
|
74
|
+
end
|
75
|
+
|
76
|
+
drop
|
77
|
+
command = "sqlite3 #{database.path} < #{project.schema_path}"
|
78
|
+
say_status :executing, command
|
79
|
+
stdout_str, stderr_str, status = Open3.capture3(command)
|
80
|
+
fail Error, "Failed loading schema: #{stderr_str}" unless stderr_str.empty?
|
81
|
+
|
82
|
+
say "Loaded schema at version #{database.current_version}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def drop
|
86
|
+
say_status :truncate, database.path, :yellow
|
87
|
+
database.drop
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
.DS_Store
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module MonkeyButler
|
2
|
+
class Util
|
3
|
+
class << self
|
4
|
+
def migration_timestamp
|
5
|
+
Time.now.strftime('%Y%m%d%H%M%S%3N').to_i
|
6
|
+
end
|
7
|
+
|
8
|
+
def migration_named(name, timestamp = migration_timestamp)
|
9
|
+
migration_name = [timestamp, Thor::Util.snake_case(name)].join('_')
|
10
|
+
end
|
11
|
+
|
12
|
+
def strip_leading_whitespace(string)
|
13
|
+
string.gsub(/^\s+/, '')
|
14
|
+
end
|
15
|
+
|
16
|
+
def migration_version_from_path(path)
|
17
|
+
path.match(/(\d{17})_/)[1].to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def migration_versions_from_paths(paths)
|
21
|
+
paths.map { |path| migration_version_from_path(path) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def migrations_by_version(paths)
|
25
|
+
paths.inject({}) do |hash, path|
|
26
|
+
version = migration_version_from_path(path)
|
27
|
+
hash[version] = path
|
28
|
+
hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def camelize(term, uppercase_first_letter = true)
|
33
|
+
string = term.to_s
|
34
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
35
|
+
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
36
|
+
string.gsub!('/', '::')
|
37
|
+
string
|
38
|
+
end
|
39
|
+
|
40
|
+
def database_named(name)
|
41
|
+
raise ArgumentError, "Database name cannot be nil." if name.nil?
|
42
|
+
require "monkey_butler/databases/#{name}_database"
|
43
|
+
klass_name = "MonkeyButler::Databases::#{Util.camelize(name)}Database"
|
44
|
+
Object.const_get(klass_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def target_classes_named(*names)
|
48
|
+
raise ArgumentError, "Database name cannot be nil." if names.nil?
|
49
|
+
names.flatten.map do |name|
|
50
|
+
require "monkey_butler/targets/#{name}/#{name}_target"
|
51
|
+
klass_name = "MonkeyButler::Targets::#{Util.camelize(name)}Target"
|
52
|
+
Object.const_get(klass_name).tap do |klass|
|
53
|
+
yield klass if block_given?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def unique_tag_for_version(version)
|
59
|
+
revision = nil
|
60
|
+
tag = nil
|
61
|
+
begin
|
62
|
+
tag = [version, revision].compact.join('.')
|
63
|
+
existing_tag = `git tag -l #{tag}`
|
64
|
+
break if existing_tag == ""
|
65
|
+
revision = revision.to_i + 1
|
66
|
+
end while true
|
67
|
+
tag
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/logo.jpg
ADDED
Binary file
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'monkey_butler/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'monkey_butler'
|
8
|
+
s.version = '1.2.2'
|
9
|
+
s.date = '2014-06-15'
|
10
|
+
s.summary = "Monkey Butler is a schema management system for SQLite"
|
11
|
+
s.description = "A simple hello world gem"
|
12
|
+
s.authors = ["Blake Watters"]
|
13
|
+
s.email = 'blake@layer.com'
|
14
|
+
s.homepage = 'http://github.com/layerhq/monkey_butler'
|
15
|
+
s.license = 'Apache 2'
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split($/)
|
18
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency 'thor', '~> 0.19.1'
|
23
|
+
s.add_dependency 'sqlite3', '~> 1.3.9'
|
24
|
+
s.add_dependency 'cql-rb', '~> 2.0.0'
|
25
|
+
|
26
|
+
s.add_development_dependency "bundler", "~> 1.6"
|
27
|
+
s.add_development_dependency "rake"
|
28
|
+
s.add_development_dependency "rspec", "~> 2.14.1"
|
29
|
+
s.add_development_dependency "guard-rspec", "~> 4.2.9"
|
30
|
+
s.add_development_dependency "simplecov", "~> 0.8.2"
|
31
|
+
s.add_development_dependency "byebug", "~> 3.1.2"
|
32
|
+
s.add_development_dependency "cocoapods", "~> 0.33.1"
|
33
|
+
end
|