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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 34a4f21d64d9a746405bab46332c7c2f3b76d28b
4
- data.tar.gz: 07f090ec0a08b41ab1b51e812a3ea838b68596a8
3
+ metadata.gz: 07dc7f889af55eb6a518f8bf00e6f86bb83d5702
4
+ data.tar.gz: fcd36b11560ffd97fe8365e7397c2b8ea9a331c9
5
5
  SHA512:
6
- metadata.gz: 21f4194bbaf569276efbc421842b79128f0fb27e633de1915342eac1c75cf3dc3c403f535b132850116aba08cbea8ed4ef98c22981411c2700cdd2123b3731af
7
- data.tar.gz: c564a8882bfde54a51797d4ea41bafa6971634f43b13f457eab249c35c2cbb9274b242078381d86ea5d0239ba7f8c5284653e8c58a4eaa3d759aace1a2a8cdb6
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.1)
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
@@ -5,4 +5,4 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
5
5
  require "bundler/setup"
6
6
  require "swift/gist"
7
7
 
8
- Swift::Gist::run ARGV
8
+ exit Swift::Gist::run(ARGV)
@@ -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
@@ -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
- mktmpdir: Dir.method(:mktmpdir),
17
- open: File.method(:open),
18
- pwd: Dir.method(:pwd),
19
- spm_package_creator: method(:spm_package_definition_from_swift_modules),
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
- watchman_command: method(:watchman_command)
21
+ stdout: $stdout.method(:puts),
22
+ watcher: method(:watch_sources)
23
23
  )
24
24
 
25
25
  swift_modules = cli.call arguments
26
- project_dir = pwd.call
26
+ tmp_dir = project_generator.call(swift_modules)
27
+ stdout.call project_art_generator.call(swift_modules, tmp_dir)
27
28
 
28
- mktmpdir.call do |tmp_dir|
29
- chdir.call(tmp_dir) do
30
- spm_project_creator.call swift_modules, project_dir: project_dir
29
+ chdir.call(tmp_dir) do
30
+ system.call('swift test')
31
+ end
31
32
 
32
- open.call('Package.swift', 'w') do |file|
33
- file.puts spm_package_creator.call(swift_modules)
34
- end
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
- system.call(watchman_command.call(tmp_dir, swift_modules))
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
- project_dir:,
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 "#{project_dir}/#{source}", "Sources/#{swift_module.name}"
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
@@ -1,5 +1,5 @@
1
1
  module Swift
2
2
  module Gist
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
@@ -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/watchman_command"
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.1
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-22 00:00:00.000000000 Z
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/watchman_command.rb
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
@@ -1,11 +0,0 @@
1
- module Swift
2
- module Gist
3
- def self.watchman_paths_from_swift_modules swift_modules
4
- swift_modules
5
- .map { |swift_module| swift_module.sources }
6
- .flatten
7
- .map { |path| %Q|'#{path}'| }
8
- .join(' ')
9
- end
10
- end
11
- end