Module: Swift::Gist

Defined in:
lib/swift/gist.rb,
lib/swift/gist/cli.rb,
lib/swift/gist/runner.rb,
lib/swift/gist/version.rb,
lib/swift/gist/file_watcher.rb,
lib/swift/gist/swift_module.rb,
lib/swift/gist/stdin_watcher.rb,
lib/swift/gist/generate_project.rb,
lib/swift/gist/format_swift_module.rb,
lib/swift/gist/spm_package_creator.rb,
lib/swift/gist/spm_project_creator.rb,
lib/swift/gist/generate_project_art.rb

Defined Under Namespace

Classes: CLIError, SwiftModule

Constant Summary collapse

VERSION =
"0.0.3"

Class Method Summary collapse

Class Method Details

.format_swift_module(swift_module) ⇒ Object

This function generates a valid swift case for each SwiftModule.

The resulting output will be something like:

.target(name: “SomeModule”) .target(name: “SomeModule”, dependencies: [“SomeOtherModule”]) .testTarget(name: “SomeModuleTests”, dependencies: [“SomeOtherModule”])



11
12
13
14
15
16
17
18
19
20
21
# File 'lib/swift/gist/format_swift_module.rb', line 11

def self.format_swift_module swift_module
    target_type = swift_module.type == :src ? 'target' : 'testTarget'
    formatted_name = %Q|name: "#{swift_module.name}"|
    formatted_dependencies = "dependencies: [%s]" % swift_module.depends_on.map { |dependency| %Q|"#{dependency}"| }.join(', ')

    %Q|.%{target_type}(%{formatted_name}%{formatted_dependencies})| % {
      target_type: target_type,
      formatted_name: formatted_name,
      formatted_dependencies: swift_module.depends_on.count > 0 ? ", #{formatted_dependencies}" : ""
    }
end

.generate_project(swift_modules, mktmpdir: Dir.method(:mktmpdir), open: File.method(:open), spm_package_creator: method(:spm_package_definition_from_swift_modules), spm_project_creator: method(:spm_project_from_swift_modules)) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/swift/gist/generate_project.rb', line 4

def self.generate_project(
  swift_modules,
  mktmpdir: Dir.method(:mktmpdir),
  open: File.method(:open),
  spm_package_creator: method(:spm_package_definition_from_swift_modules),
  spm_project_creator: method(:spm_project_from_swift_modules)
)

  mktmpdir.call.tap do |tmp_dir|
    spm_project_creator.call swift_modules, tmp_dir: tmp_dir

    open.call("#{tmp_dir}/Package.swift", 'w') do |file|
      file.puts spm_package_creator.call(swift_modules)
    end
  end

end

.generate_project_art(swift_modules, tmp_dir) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/swift/gist/generate_project_art.rb', line 3

def self.generate_project_art swift_modules, tmp_dir
  description = StringIO.new
  description.puts tmp_dir
  description.puts '.'
  description.puts "├── Package.swift"
  description.puts "└── Sources"
  swift_modules.sort_by { |swift_module| swift_module.name }.each_with_index do |swift_module, index|
    description.print index == swift_modules.length - 1 ? "    └── " : "    ├── "
    description.puts swift_module.name

    swift_module.sources.sort.each_with_index do |source, source_index|
      description.print index == swift_modules.length - 1               ? "     " : ""
      description.print source_index == swift_module.sources.length - 1 ? "   └── " : "   ├── "
      description.puts source
    end
  end
  description.string
end

.parse_command_line_arguments(arguments) ⇒ Object

The CLI creates a new module for every ‘–module` or `–test-module` argument. The `–source` and `–depends-on` arguments are all applied to the last defined swift module.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/swift/gist/cli.rb', line 16

def self.parse_command_line_arguments arguments
  unless arguments.count > 0
    error = CLIError.new(<<REASON
Usage: swift-gist [--module[=<name>]] [--test-module[=<name>]]
              [--source[=<glob>]] [--depends-on[=<module name>]]
REASON
)
    raise error
  end

  swift_modules = []

  OptionParser.new do |parser|
    parser.on('--module MODULE') do |module_name|
      swift_modules << SwiftModule.new(module_name, :src, [])
    end

    parser.on('--source SOURCE') do |source|
      if swift_modules.last.nil?
        raise CLIError.new("Error: The `--source` argument requires that a `--module` or `--test-module` has already been defined.")
      end

      swift_modules.last.sources |= Dir[source]
    end

    parser.on('--test-module MODULE') do |module_name|
      swift_modules << SwiftModule.new(module_name, :test, [])
    end

    parser.on('--depends-on MODULE') do |module_name|
      if swift_modules.last.nil?
        raise CLIError.new("Error: The `--depends-on` argument requires that a `--module` or `--test-module` has already been defined.")
      end
      swift_modules.last.depends_on << module_name
    end
  end.parse arguments

  swift_modules.each do |swift_module|
    if swift_module.sources.count == 0
      raise CLIError.new("Error: The module '#{swift_module.name}' does not have any valid sources.")
    end
  end

  swift_modules
end

.run(arguments, chdir: Dir.method(:chdir), cli: method(:parse_command_line_arguments), formatted_date: -> { Time.iso8601(Time.now.iso8601) }, project_art_generator: method(:generate_project_art), project_generator: method(:generate_project), rm_rf: FileUtils.method(:rm_rf), stdin_watcher: method(:watch_stdin), stdout: $stdout.method(:puts), system: method(:system), watcher: method(:watch_sources)) ⇒ Object

This is the ugly command that binds everything together.

1 - Parses command line arguments 2 - Creates a SPM project linking just the files specified with ‘–source` arguments



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/swift/gist/runner.rb', line 12

def self.run(
  arguments,
  chdir: Dir.method(:chdir),
  cli: method(:parse_command_line_arguments),
  formatted_date: -> { Time.iso8601(Time.now.iso8601) },
  project_art_generator: method(:generate_project_art),
  project_generator: method(:generate_project),
  rm_rf: FileUtils.method(:rm_rf),
  stdin_watcher: method(:watch_stdin),
  stdout: $stdout.method(:puts),
  system: method(:system),
  watcher: method(:watch_sources)
)

  begin
    swift_modules = cli.call arguments
  rescue CLIError => error
    puts error.reason
    exit
  end

  begin
    tmp_dir = project_generator.call(swift_modules)
    stdout.call project_art_generator.call(swift_modules, tmp_dir)

    chdir.call(tmp_dir) do
      system.call('swift test')
    end

    counter = 1

    build_command = -> {
      stdout.call "\n\n-----> Running `$ swift test` @ '#{formatted_date.call}' - Build ##{counter}"
      chdir.call(tmp_dir) do
        system.call('swift test')
      end

      counter += 1
    }

    watcher.call(swift_modules, &build_command)
    stdin_watcher.call(&build_command)

  ensure
    rm_rf.call tmp_dir
  end

  0
end

.spm_package_definition_from_swift_modules(swift_modules, format_swift_module: method(:format_swift_module)) ⇒ Object



5
6
7
8
# File 'lib/swift/gist/spm_package_creator.rb', line 5

def self.spm_package_definition_from_swift_modules swift_modules, format_swift_module: method(:format_swift_module)
  formatted_modules = swift_modules.map { |swift_module| format_swift_module.call swift_module }
  ERB.new(ERB_TEMPLATE).result(binding)
end

.spm_project_from_swift_modules(swift_modules, tmp_dir:, mkdir_p: FileUtils.method(:mkdir_p), pwd: Dir.method(:pwd), ln_s: FileUtils.method(:ln_s)) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/swift/gist/spm_project_creator.rb', line 4

def self.spm_project_from_swift_modules(
  swift_modules,
  tmp_dir:,
  mkdir_p: FileUtils.method(:mkdir_p),
  pwd: Dir.method(:pwd),
  ln_s: FileUtils.method(:ln_s)
)

  swift_modules.each do |swift_module|
    mkdir_p.call "#{tmp_dir}/Sources/#{swift_module.name}"
    swift_module.sources.each do |source|
      ln_s.call "#{pwd.call}/#{source}", "#{tmp_dir}/Sources/#{swift_module.name}"
    end
  end
end

.watch_sources(swift_modules, listener: Listen) ⇒ Object



5
6
7
8
9
10
11
12
13
# File 'lib/swift/gist/file_watcher.rb', line 5

def self.watch_sources swift_modules, listener: Listen
  sources = Set.new(swift_modules.map { |swift_module| swift_module.sources }.flatten)
  dirs    = Set.new(sources.map { |path| Dir.exist?(path) ? path : File.dirname(path) })

  listener.to(*dirs, only: /.*swift/, relative: true) do |modified, created, deleted|
    next unless sources.intersect?(Set.new(modified + created + deleted))
    yield
  end.start
end

.watch_stdin(stdin: STDIN) ⇒ Object



4
5
6
7
8
# File 'lib/swift/gist/stdin_watcher.rb', line 4

def self.watch_stdin stdin: STDIN
  while stdin.gets
    yield
  end
end