swift-gist 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +11 -1
- data/README.md +79 -1
- data/bin/swift-gist +1 -1
- data/lib/swift/gist/generate_project.rb +23 -0
- data/lib/swift/gist/generate_project_art.rb +22 -0
- data/lib/swift/gist/runner.rb +22 -15
- data/lib/swift/gist/spm_project_creator.rb +4 -3
- data/lib/swift/gist/swift_module.rb +0 -4
- data/lib/swift/gist/version.rb +1 -1
- data/lib/swift/gist/watcher.rb +17 -0
- data/lib/swift/gist.rb +3 -2
- data/swift-gist.gemspec +2 -0
- metadata +19 -4
- data/lib/swift/gist/watchman_command.rb +0 -8
- data/lib/swift/gist/watchman_paths.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07dc7f889af55eb6a518f8bf00e6f86bb83d5702
|
4
|
+
data.tar.gz: fcd36b11560ffd97fe8365e7397c2b8ea9a331c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0312a7227f6f66da89ec6223f221b4eb0dfe8d575ecdc3f731669eac79b67e0ab71413c81fa03fb475e54de9db974f8dfc3c0f0d3cb2e39c7824e17f542b29af
|
7
|
+
data.tar.gz: 622b8e3935c07d9ca07e0d93ac06ba23bba5e935ae9f9366ca63f77955981a7280f6b58c7b3526b36bb4c6974414084d68c4bd39643f047f1544994b8daad52e
|
data/Gemfile.lock
CHANGED
@@ -1,15 +1,25 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
swift-gist (0.0.
|
4
|
+
swift-gist (0.0.2)
|
5
|
+
listen (~> 3.1.5)
|
5
6
|
|
6
7
|
GEM
|
7
8
|
remote: https://rubygems.org/
|
8
9
|
specs:
|
9
10
|
docile (1.3.0)
|
11
|
+
ffi (1.9.23)
|
10
12
|
json (2.1.0)
|
13
|
+
listen (3.1.5)
|
14
|
+
rb-fsevent (~> 0.9, >= 0.9.4)
|
15
|
+
rb-inotify (~> 0.9, >= 0.9.7)
|
16
|
+
ruby_dep (~> 1.2)
|
11
17
|
minitest (5.11.3)
|
12
18
|
rake (10.5.0)
|
19
|
+
rb-fsevent (0.10.3)
|
20
|
+
rb-inotify (0.9.10)
|
21
|
+
ffi (>= 0.5.0, < 2)
|
22
|
+
ruby_dep (1.5.0)
|
13
23
|
simplecov (0.16.1)
|
14
24
|
docile (~> 1.1)
|
15
25
|
json (>= 1.8, < 3)
|
data/README.md
CHANGED
@@ -1 +1,79 @@
|
|
1
|
-
|
1
|
+
# swift-gist
|
2
|
+
|
3
|
+
A tool to reduce feedback cycles when doing TDD with Swift.
|
4
|
+
|
5
|
+
TDD for iOS can be extremely painful as the feedback cycle (time from save to test results) is just too slow. The slow downs come from things like building lots of source code, code signing and simulator startup.
|
6
|
+
|
7
|
+
This tool can help in certain cases to get feedback cycles to acceptable levels. The basic idea is that you manually (sounds painful but it's not too bad) pick the files you want to build and test, the fewer the better. Run `swift-gist` and it will create an SPM project in a tmp directory that links to just the files you specify and then starts to watch these files and run tests whenever a file is saved. The heavy lifting is picked up by SPM so this means you cannot specify files that `import UIKit`. It's also advisable to keep dependencies to a minimum (which I guess is just normally good advice).
|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
Invoke the `swift-gist` command and tell it about how you want your project set up (you can always save this command in a nice shell script for reuse later).
|
14
|
+
|
15
|
+
The first argument should be either `--module` or `--test-module` followed by a name for the module. It's best to mirror your projects current module naming e.g. if I have a project called `MyApp` then I would use `--module MyApp` as the tests will be looking to `import MyApp`.
|
16
|
+
|
17
|
+
After you have defined a module you should add some source code. You can invoke `--source` with quoted globs (quoted so that your shell does not automatically expand them) as many times as you like. The globs will be expanded with the resulting paths being combined and added to the last defined module.
|
18
|
+
|
19
|
+
Finally you can have as many `--depends-on` arguments as you like. Multiple invocations will be added to the last defined module. On a simple project you'll need to use this to tell the test module about the module it is testing.
|
20
|
+
|
21
|
+
That was a lot of text - how about an example.
|
22
|
+
|
23
|
+
Given this project:
|
24
|
+
|
25
|
+
```
|
26
|
+
MyApp
|
27
|
+
├── Source
|
28
|
+
│ └── FeatureA
|
29
|
+
│ ├── ...
|
30
|
+
│ ├── hundreds of other files
|
31
|
+
│ ├── ...
|
32
|
+
│ └── ImportantClass.swift
|
33
|
+
└── Tests
|
34
|
+
└── FeatureA
|
35
|
+
├── ...
|
36
|
+
├── hundreds of other files
|
37
|
+
├── ...
|
38
|
+
└── ImportantClassTests.swift
|
39
|
+
```
|
40
|
+
|
41
|
+
If I am looking to do a tight TDD loop on the `ImportantClass` file and it only depends on `Foundation` I can use the following invocation.
|
42
|
+
|
43
|
+
```
|
44
|
+
swift-gist --module MyApp \
|
45
|
+
--source 'Source/Feature/ImportantClass.swift' \
|
46
|
+
--test-module MyAppTests \
|
47
|
+
--source 'Tests/Feature/ImportantClassTests.swift' \
|
48
|
+
--depends-on MyApp
|
49
|
+
```
|
50
|
+
|
51
|
+
This will cause my shell to start watching for file saves. Every time a file is saved it will run the unit tests.
|
52
|
+
|
53
|
+
---
|
54
|
+
|
55
|
+
## Installation
|
56
|
+
|
57
|
+
`gem install swift-gist`
|
58
|
+
|
59
|
+
---
|
60
|
+
|
61
|
+
## Why make this tool?
|
62
|
+
|
63
|
+
TDD really requires sub second cycle times for optimal flow. Even small projects can take multiple minutes to build and test - large projects are much worse. This leads to the tendency to take bigger steps in your development flow to batch work up instead of paying the cost of a build/test cycle. This tool aims to allow you (with a little manual effort) to finely craft what you want to build and test to suit what you are working on here and now. That could be specifying a specific file or building more elaborate watches.
|
64
|
+
|
65
|
+
## Why not do the whole modularization thing that is so popular right now?
|
66
|
+
|
67
|
+
Personally I think the move towards modularization is great for development speed and engineering good practices like defining boundaries and isolating parts of your app. The reason for `swift-gist` is that modularization is cool but for most people it's either going to be a very slow transformation or not viable but they are still feeling pain now. This is an attempt to help sooth the pain a little without requiring a massive reshaping of your existing code base.
|
68
|
+
|
69
|
+
## Why make this in Ruby?
|
70
|
+
|
71
|
+
I've kept this to not using any fancy ruby dependencies, so if you are using Sierra or High Sierra you already have everything you need to use this tool. Although it's possible to write this in Swift - I think it's easier to write `gem install swift-gist` than it is to figure out how to package this for Swift.
|
72
|
+
|
73
|
+
---
|
74
|
+
|
75
|
+
## I found a bug - what do I do?
|
76
|
+
|
77
|
+
Sweet, thanks for trying this tool out - could you spare some time to raise an issue? Likelihood is it's because the tool is a rough proof of concept and needs hardening to common issues and better documentation.
|
78
|
+
|
79
|
+
If you can write ruby - submit a PR and I'll be happy to look at it. If you are going to write a massive PR it might be worth discussing the proposed changes first to avoid disappointment if the changes don't get accepted.
|
data/bin/swift-gist
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Swift
|
2
|
+
module Gist
|
3
|
+
|
4
|
+
def self.generate_project(
|
5
|
+
swift_modules,
|
6
|
+
mktmpdir: Dir.method(:mktmpdir),
|
7
|
+
open: File.method(:open),
|
8
|
+
spm_package_creator: method(:spm_package_definition_from_swift_modules),
|
9
|
+
spm_project_creator: method(:spm_project_from_swift_modules)
|
10
|
+
)
|
11
|
+
|
12
|
+
mktmpdir.call.tap do |tmp_dir|
|
13
|
+
spm_project_creator.call swift_modules, tmp_dir: tmp_dir
|
14
|
+
|
15
|
+
open.call("#{tmp_dir}/Package.swift", 'w') do |file|
|
16
|
+
file.puts spm_package_creator.call(swift_modules)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Swift
|
2
|
+
module Gist
|
3
|
+
def self.generate_project_art swift_modules, tmp_dir
|
4
|
+
description = StringIO.new
|
5
|
+
description.puts tmp_dir
|
6
|
+
description.puts '.'
|
7
|
+
description.puts "├── Package.swift"
|
8
|
+
description.puts "└── Sources"
|
9
|
+
swift_modules.sort_by { |swift_module| swift_module.name }.each_with_index do |swift_module, index|
|
10
|
+
description.print index == swift_modules.length - 1 ? " └── " : " ├── "
|
11
|
+
description.puts swift_module.name
|
12
|
+
|
13
|
+
swift_module.sources.sort.each_with_index do |source, source_index|
|
14
|
+
description.print index == swift_modules.length - 1 ? " " : " │"
|
15
|
+
description.print source_index == swift_module.sources.length - 1 ? " └── " : " ├── "
|
16
|
+
description.puts source
|
17
|
+
end
|
18
|
+
end
|
19
|
+
description.string
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/swift/gist/runner.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'fileutils'
|
2
|
+
require 'time'
|
2
3
|
require 'tmpdir'
|
3
4
|
|
4
5
|
module Swift
|
@@ -8,35 +9,41 @@ module Swift
|
|
8
9
|
#
|
9
10
|
# 1 - Parses command line arguments
|
10
11
|
# 2 - Creates a SPM project linking just the files specified with `--source` arguments
|
11
|
-
# 3 - Starts watchman
|
12
12
|
def self.run(
|
13
13
|
arguments,
|
14
14
|
chdir: Dir.method(:chdir),
|
15
15
|
cli: method(:parse_command_line_arguments),
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
spm_project_creator: method(:spm_project_from_swift_modules),
|
16
|
+
formatted_date: -> { Time.iso8601(Time.now.iso8601) },
|
17
|
+
project_art_generator: method(:generate_project_art),
|
18
|
+
project_generator: method(:generate_project),
|
19
|
+
rm_rf: FileUtils.method(:rm_rf),
|
21
20
|
system: method(:system),
|
22
|
-
|
21
|
+
stdout: $stdout.method(:puts),
|
22
|
+
watcher: method(:watch_sources)
|
23
23
|
)
|
24
24
|
|
25
25
|
swift_modules = cli.call arguments
|
26
|
-
|
26
|
+
tmp_dir = project_generator.call(swift_modules)
|
27
|
+
stdout.call project_art_generator.call(swift_modules, tmp_dir)
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
chdir.call(tmp_dir) do
|
30
|
+
system.call('swift test')
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
counter = 1
|
34
|
+
watcher.call(swift_modules) do
|
35
|
+
stdout.call "\n\n-----> Running `$ swift test` @ '#{formatted_date.call}' - Build ##{counter}"
|
36
|
+
chdir.call(tmp_dir) do
|
37
|
+
system.call('swift test')
|
35
38
|
end
|
36
39
|
|
37
|
-
|
40
|
+
counter += 1
|
38
41
|
end
|
39
42
|
|
43
|
+
0
|
44
|
+
|
45
|
+
ensure
|
46
|
+
rm_rf.call tmp_dir
|
40
47
|
end
|
41
48
|
|
42
49
|
end
|
@@ -3,15 +3,16 @@ module Swift
|
|
3
3
|
|
4
4
|
def self.spm_project_from_swift_modules(
|
5
5
|
swift_modules,
|
6
|
-
|
6
|
+
tmp_dir:,
|
7
7
|
mkdir_p: FileUtils.method(:mkdir_p),
|
8
|
+
pwd: Dir.method(:pwd),
|
8
9
|
ln_s: FileUtils.method(:ln_s)
|
9
10
|
)
|
10
11
|
|
11
12
|
swift_modules.each do |swift_module|
|
12
|
-
mkdir_p.call "Sources/#{swift_module.name}"
|
13
|
+
mkdir_p.call "#{tmp_dir}/Sources/#{swift_module.name}"
|
13
14
|
swift_module.sources.each do |source|
|
14
|
-
ln_s.call "#{
|
15
|
+
ln_s.call "#{pwd.call}/#{source}", "#{tmp_dir}/Sources/#{swift_module.name}"
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
@@ -6,10 +6,6 @@ module Swift
|
|
6
6
|
def initialize name, type, sources, depends_on=[]
|
7
7
|
@name, @type, @sources, @depends_on = name, type, sources, depends_on
|
8
8
|
end
|
9
|
-
|
10
|
-
def == other
|
11
|
-
name == other.name && sources == other.sources && depends_on == other.depends_on && type == other.type
|
12
|
-
end
|
13
9
|
end
|
14
10
|
end
|
15
11
|
end
|
data/lib/swift/gist/version.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'listen'
|
2
|
+
|
3
|
+
module Swift
|
4
|
+
module Gist
|
5
|
+
def self.watch_sources swift_modules, listener: Listen, sleep: method(:sleep)
|
6
|
+
sources = Set.new(swift_modules.map { |swift_module| swift_module.sources }.flatten)
|
7
|
+
dirs = Set.new(sources.map { |path| Dir.exist?(path) ? path : File.dirname(path) })
|
8
|
+
|
9
|
+
listener.to(*dirs, only: /.*swift/, relative: true) do |modified, created, deleted|
|
10
|
+
next unless sources.intersect?(Set.new(modified + created + deleted))
|
11
|
+
yield
|
12
|
+
end.start
|
13
|
+
|
14
|
+
sleep.call
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/swift/gist.rb
CHANGED
@@ -5,10 +5,11 @@ end
|
|
5
5
|
|
6
6
|
require "swift/gist/cli"
|
7
7
|
require "swift/gist/format_swift_module"
|
8
|
+
require "swift/gist/generate_project"
|
9
|
+
require "swift/gist/generate_project_art"
|
8
10
|
require "swift/gist/runner"
|
9
11
|
require "swift/gist/spm_package_creator"
|
10
12
|
require "swift/gist/spm_project_creator"
|
11
13
|
require "swift/gist/swift_module"
|
12
14
|
require "swift/gist/version"
|
13
|
-
require "swift/gist/
|
14
|
-
require "swift/gist/watchman_paths"
|
15
|
+
require "swift/gist/watcher"
|
data/swift-gist.gemspec
CHANGED
@@ -21,6 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
+
spec.add_runtime_dependency 'listen', '~> 3.1.5'
|
25
|
+
|
24
26
|
spec.add_development_dependency "bundler", "~> 1.16"
|
25
27
|
spec.add_development_dependency "minitest", "~> 5.0"
|
26
28
|
spec.add_development_dependency "rake", "~> 10.0"
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: swift-gist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Samuels
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: listen
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.1.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.1.5
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,13 +100,14 @@ files:
|
|
86
100
|
- lib/swift/gist.rb
|
87
101
|
- lib/swift/gist/cli.rb
|
88
102
|
- lib/swift/gist/format_swift_module.rb
|
103
|
+
- lib/swift/gist/generate_project.rb
|
104
|
+
- lib/swift/gist/generate_project_art.rb
|
89
105
|
- lib/swift/gist/runner.rb
|
90
106
|
- lib/swift/gist/spm_package_creator.rb
|
91
107
|
- lib/swift/gist/spm_project_creator.rb
|
92
108
|
- lib/swift/gist/swift_module.rb
|
93
109
|
- lib/swift/gist/version.rb
|
94
|
-
- lib/swift/gist/
|
95
|
-
- lib/swift/gist/watchman_paths.rb
|
110
|
+
- lib/swift/gist/watcher.rb
|
96
111
|
- swift-gist.gemspec
|
97
112
|
homepage: https://github.com/paulsamuels/swift-gist
|
98
113
|
licenses:
|
@@ -1,8 +0,0 @@
|
|
1
|
-
module Swift
|
2
|
-
module Gist
|
3
|
-
def self.watchman_command dir, swift_modules, watchman_paths_from_swift_modules: method(:watchman_paths_from_swift_modules)
|
4
|
-
sources = watchman_paths_from_swift_modules.call(swift_modules)
|
5
|
-
"watchman-make -p #{sources} --run 'cd #{dir} && swift test'"
|
6
|
-
end
|
7
|
-
end
|
8
|
-
end
|