swift-playground 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +19 -0
  5. data/LICENSE +22 -0
  6. data/README.md +209 -0
  7. data/Rakefile +16 -0
  8. data/bin/swift-playground +5 -0
  9. data/lib/swift/playground.rb +193 -0
  10. data/lib/swift/playground/asset.rb +52 -0
  11. data/lib/swift/playground/assets/javascript.rb +9 -0
  12. data/lib/swift/playground/assets/stylesheet.rb +36 -0
  13. data/lib/swift/playground/cli.rb +29 -0
  14. data/lib/swift/playground/cli/commands/generate.rb +95 -0
  15. data/lib/swift/playground/cli/commands/new.rb +49 -0
  16. data/lib/swift/playground/cli/definition.rb +32 -0
  17. data/lib/swift/playground/cli/global/error_handling.rb +34 -0
  18. data/lib/swift/playground/cli/shared_attributes.rb +19 -0
  19. data/lib/swift/playground/cli/ui.rb +120 -0
  20. data/lib/swift/playground/debug.rb +4 -0
  21. data/lib/swift/playground/generator.rb +32 -0
  22. data/lib/swift/playground/metadata.rb +12 -0
  23. data/lib/swift/playground/section.rb +117 -0
  24. data/lib/swift/playground/sections/code_section.rb +19 -0
  25. data/lib/swift/playground/sections/documentation_section.rb +63 -0
  26. data/lib/swift/playground/template/Documentation/defaults.css.scss +96 -0
  27. data/lib/swift/playground/template/Documentation/section.html.erb +23 -0
  28. data/lib/swift/playground/template/contents.xcplayground.erb +8 -0
  29. data/lib/swift/playground/util.rb +3 -0
  30. data/lib/swift/playground/util/path_or_content.rb +31 -0
  31. data/lib/swift/playground/util/pipeline.rb +53 -0
  32. data/lib/swift/playground/util/pipeline/section_filter.rb +55 -0
  33. data/lib/swift/playground/util/pipeline/unicode_emoji_filter.rb +27 -0
  34. data/lib/swift/playground/util/syntax_highlighting.rb +24 -0
  35. data/swift-playground.gemspec +29 -0
  36. metadata +207 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 992b070655de9c6fc56513c9e7f94e1def404707
4
+ data.tar.gz: b5af362cd26b24bfc49335c2610a5812b79ba521
5
+ SHA512:
6
+ metadata.gz: 2b797a867775fffa37d24eda66c247e49a146c2cb47cff74d62aa12a27fae38a757db3696d75f246202d3d7f1558418bb0115ea298f6b09c75a04ac51a572c9c
7
+ data.tar.gz: d1c125605c84a4ba04e7e6290b92240cd774ad127dc2e52b33daefab4a29487663bf9aefc94a2bbe01c76232269a0a49674337a7c5b23fc1e6b83c761dcce751
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+
7
+ group :development do
8
+ # github-linguist requires charlock_holmes which is not an easy install, making
9
+ # this an optional dependency gives gem users the choice of whether to solve
10
+ # the problem of installing that gem in order to get syntax highlighting.
11
+ #
12
+ # pygments.rb can also be problematic on some platforms and also is used
13
+ # only for syntax highlighting so can be optional also:
14
+ gem 'github-linguist', '~> 4.3.1'
15
+ gem 'pygments.rb', '~> 0.6.0'
16
+
17
+ gem 'pry'
18
+ gem 'pry-byebug', '1.3.3'
19
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Resolve Digital and Mark Haylock
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,209 @@
1
+ # Swift Playground
2
+
3
+ Create and modify Xcode Swift Playgrounds from Ruby. Includes both a Ruby API and a CLI.
4
+
5
+ <!-- START doctoc generated TOC please keep comment here to allow auto update -->
6
+ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
7
+ ## Contents
8
+
9
+ - [Installation](#installation)
10
+ - [CLI Usage](#cli-usage)
11
+ - [Creating an empty playground](#creating-an-empty-playground)
12
+ - [Generate a playground from markdown](#generate-a-playground-from-markdown)
13
+ - [Ruby Usage](#ruby-usage)
14
+ - [Constructing a basic playground](#constructing-a-basic-playground)
15
+ - [Generating a playground from markdown](#generating-a-playground-from-markdown)
16
+ - [Sections](#sections)
17
+ - [Markdown Format](#markdown-format)
18
+ - [Default Stylesheet](#default-stylesheet)
19
+ - [Credits](#credits)
20
+
21
+ <!-- END doctoc generated TOC please keep comment here to allow auto update -->
22
+
23
+ ## Installation
24
+
25
+ Install via RubyGems:
26
+ ```
27
+ $ gem install playground
28
+ ```
29
+
30
+ ## CLI Usage
31
+
32
+ ### Creating an empty playground
33
+
34
+ The playground created is the same as as Xcode would via "File > New > Playground…":
35
+ ```
36
+ $ swift-playground new [options] example.playground
37
+ ```
38
+
39
+ This command supports these options (see `swift-playground help new`):
40
+
41
+ * __`--platform=[ios|osx]`__
42
+
43
+ The target platform for the generated playground (default: ios).
44
+ * __`--[no-]reset`__
45
+
46
+ Allow the playground to be reset to it's original state via "Editor > Reset Playground" in Xcode (default: enabled).
47
+ * __`--open`__
48
+
49
+ Open the playground in Xcode once it has been created.
50
+
51
+ ### Generate a playground from markdown
52
+
53
+ ```
54
+ $ swift-playground generate [options] example.md
55
+ ```
56
+
57
+ This command supports the following options (see `swift-playground help generate`) in addition to the options supported by the `new` command that are detailed above:
58
+
59
+ * __`--stylesheet=<file>`__
60
+
61
+ CSS stylesheet for the HTML documentation sections of the playground. [SASS/SCSS syntax](http://sass-lang.com) is supported. This will be included after the default stylesheet. (default: none).
62
+ * __`--javascript=<file>`__
63
+
64
+ A javascript file for the HTML documentation sections of the playground. Each section is rendered independently of another and the script will not have access to the DOM from any other sections (default: none).
65
+ * __`--[no-]reset`__
66
+
67
+ Allow the playground to be reset to it's original state via "Editor > Reset Playground" in Xcode (default: enabled).
68
+ * __`--open`__
69
+
70
+ Open the playground in Xcode once it has been created.
71
+ * __`--[no-]emoji`__
72
+
73
+ Convert emoji aliases (e.g. `:+1:`) into emoji characters (default: enabled).
74
+ * __`--[no-]highlighting`__
75
+
76
+ Detect non-swift code blocks and add syntax highlighting. Only has an effect if '[github-linguist](https://github.com/github/linguist)' and '[pygments.rb](https://github.com/tmm1/pygments.rb)' gems are installed. (default: enabled).
77
+ * __`--highlighting-style=<style>`__
78
+
79
+ The name of a pygments (http://pygments.org/) style to apply to syntax highlighted code blocks. Set to 'custom' if providing your own pygments-compatible stylesheet. Ignored if `--no-highlighting` is set. (default: default).
80
+
81
+ ## Ruby Usage
82
+
83
+ _WARNING: This is still under development and the API may change before 1.0 is released._
84
+
85
+ ### Constructing a basic playground
86
+
87
+ ```ruby
88
+ require 'swift/playground'
89
+
90
+ playground = Swift::Playground.new
91
+
92
+ documentation = Swift::Playground::DocumentationSection.new <<-HTML
93
+ <h1>Welcome to the Playground!</h1>
94
+ <p>
95
+ This is an <em>awesome</em> example playground!
96
+ </p>
97
+ HTML
98
+
99
+ code = Swift::Playground::CodeSection.new <<-SWIFT
100
+ // Write swiftly!
101
+
102
+ import UIKit
103
+
104
+ var str = "This string has contents."
105
+ SWIFT
106
+
107
+ playground.sections << documentation
108
+ playground.sections << code
109
+
110
+ playground.save('~/example.playground')
111
+ ```
112
+
113
+ ### Generating a playground from markdown
114
+
115
+ ```ruby
116
+ require 'swift/playground/generator'
117
+
118
+ playground = Swift::Playground::Generator.generate(markdown_file)
119
+ ```
120
+
121
+ ### Sections
122
+
123
+ There are two section types you can use to construct a playground in Ruby:
124
+
125
+ #### `DocumentationSection`
126
+
127
+ These contain HTML that is rendered within the playground. You can construct a `DocumentationSection` with either a path to an HTML file or the raw HTML content itself (either as a String or an IO object):
128
+
129
+ ```ruby
130
+ # All of the following are valid values for content:
131
+ content = '/path/to/file.html'
132
+ content = Pathname.new('/path/to/file.html')
133
+ content = File.open('/path/to/file.html')
134
+ content = <<-HTML
135
+ <h1>An example HTML fragment</h1>
136
+ <p>
137
+ Note this is a fragment, it does not have a root 'html' or 'body' tag.
138
+ </p>
139
+ HTML
140
+
141
+ # Creating the section:
142
+ section = Swift::Playground::DocumentationSection.new(content)
143
+
144
+ # Adding the section to a playground:
145
+ playground.sections << section
146
+ # or perhaps:
147
+ playground.sections.insert(0, section)
148
+ ```
149
+
150
+ The content you provide _must_ be an HTML fragment - if a `<html>`, `<head>` or `<body>` tag is present an exception will be raised.
151
+
152
+ #### `CodeSection`
153
+
154
+ These contain the executable swift code, and each playground must contain at least one of these sections. Constructing these sections is the same as `DocumentationSection` - you can use either a path to a swift file, or the raw swift code itself (either as a String or an IO object):
155
+
156
+ ```ruby
157
+ # All of the following are valid values for content:
158
+ content = '/path/to/file.swift'
159
+ content = Pathname.new('/path/to/file.swift')
160
+ content = File.open('/path/to/file.swift')
161
+ content = <<-SWIFT
162
+ // Write swiftly!
163
+
164
+ import UIKit
165
+
166
+ var str = "This string has contents."
167
+ SWIFT
168
+
169
+ # Creating the section:
170
+ section = Swift::Playground::CodeSection.new(content)
171
+
172
+ # Set the 'style' of the section. Apple only document 'setup' at the
173
+ # moment and this is all that is supported.
174
+ #
175
+ # 'setup' will wrap the section in a "Setup" label that can be toggled
176
+ # (and initially appears minimized):
177
+ section.style = 'setup'
178
+
179
+ # Adding the section to a playground:
180
+ playground.sections << section
181
+ # or perhaps:
182
+ playground.sections.insert(0, section)
183
+ ```
184
+
185
+ ## Markdown Format
186
+
187
+ Generating a playground from Markdown supports the [Github Flavoured Markdown](https://help.github.com/articles/github-flavored-markdown/) syntax.
188
+
189
+ ## Default Stylesheet
190
+
191
+ Each documentation section is generated with a [default stylesheet](lib/swift/playground/template/Documentation/defaults.css.scss) loaded before any custom stylesheet. This stylesheet aims to provide an improved baseline over what is already there from the webkit renderer's agent stylesheets.
192
+
193
+ It does so in some specific ways:
194
+ 1. The default font size is adjusted so that at `1rem` the default Xcode font "Menlo" will render at exactly the same size as it would in the swift code sections of the playground. This is true even if the user has changed the editor font size.
195
+ 2. A "gutter" (`body > .gutter`) is added that renders a line where the editor gutter appears in Xcode when line numbers are disabled. When Line numbers are visible it will appear centered with the line numbers.
196
+ 3. The main body of the section (`body > section`) has a left and right padding that aligns the text with the left hand margin of swift code sections.
197
+ 4. Sets the default font of `<code>` and `<pre>` elements to "Menlo" which matches with the current default Xcode font.
198
+
199
+ _Warning: These features rely on aspects of the Xcode interface the could change in future versions: the width of the editor gutter and the default size of the font inside of HTML documentation sections. There are no guarantees that these will stay the same between Xcode versions._
200
+
201
+ _Be particularly cautious with adding color to the gutter, those colors may clash with the real gutter in the swift code sections - depending on the Theme being used by the user._
202
+
203
+ ## Credits
204
+
205
+ Initial development by [Mark Haylock](https://github.com/mhaylock). Development sponsored by [Resolve Digital](http://resolve.digital).
206
+
207
+ This work was originally inspired by the work of [Jason Sandmeyer](https://github.com/jas) (created [Playground](https://github.com/jas/playground) using Node.js) and [Sam Soffes](https://github.com/soffes) (created [Playground](https://github.com/soffes/playground) using Ruby) - thank you to you both!
208
+
209
+ Thank you to [Amanda Wagener](https://github.com/awagener) for some pair programming assistance at [Rails Camp NZ 5](http://railscamps.com).
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ module Bundler
2
+ class GemHelper
3
+ def perform_git_push_with_clean_env(options = '')
4
+ # Using a clean ENV ensures that ruby-based git credential helpers
5
+ # such as that used by boxen will still work:
6
+ Bundler.with_clean_env do
7
+ perform_git_push_without_clean_env(options)
8
+ end
9
+ end
10
+
11
+ alias_method :perform_git_push_without_clean_env, :perform_git_push
12
+ alias_method :perform_git_push, :perform_git_push_with_clean_env
13
+ end
14
+ end
15
+
16
+ require 'bundler/gem_tasks'
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'swift/playground'
3
+ require 'swift/playground/cli'
4
+
5
+ exit Swift::Playground::CLI.run(ARGV)
@@ -0,0 +1,193 @@
1
+ require 'fileutils'
2
+ require 'tmpdir'
3
+
4
+ require 'swift/playground/metadata'
5
+ require 'swift/playground/debug'
6
+ require 'swift/playground/generator'
7
+ require 'swift/playground/section'
8
+ require 'swift/playground/asset'
9
+
10
+ module Swift
11
+ class Playground
12
+ class TemplateContext
13
+ extend Forwardable
14
+ def_delegators :@playground, :sdk, :allows_reset?, :sections
15
+
16
+ def self.context(*args)
17
+ new(*args).instance_eval { binding }
18
+ end
19
+
20
+ def initialize(playground)
21
+ @playground = playground
22
+ end
23
+ end
24
+
25
+ attr_accessor :platform, :allow_reset, :convert_emoji, :syntax_highlighting
26
+ attr_accessor :sections, :stylesheets, :javascripts
27
+
28
+ class << self
29
+ protected
30
+
31
+ def template_path(*filenames)
32
+ template_root = Pathname.new('../playground/template').expand_path(__FILE__)
33
+ template_root.join(*filenames)
34
+ end
35
+
36
+ def xcplayground_template
37
+ unless defined? @template
38
+ template_path = template_path('contents.xcplayground.erb')
39
+ @template = ERB.new(template_path.read)
40
+ end
41
+
42
+ @template
43
+ end
44
+ end
45
+
46
+ def initialize(options = {})
47
+ self.sections = []
48
+ self.stylesheets = []
49
+ self.javascripts = []
50
+
51
+ stylesheet_path = template_path 'Documentation', 'defaults.css.scss'
52
+ self.stylesheets << Stylesheet.new(stylesheet_path)
53
+
54
+ options = {
55
+ platform: 'ios',
56
+ allow_reset: true,
57
+ convert_emoji: true,
58
+ syntax_highlighting: true
59
+ }.merge(options)
60
+
61
+ self.platform = options[:platform]
62
+ self.allow_reset = options[:allow_reset]
63
+ self.convert_emoji = options[:convert_emoji]
64
+ self.syntax_highlighting = options[:syntax_highlighting]
65
+ end
66
+
67
+ def platform=(platform)
68
+ platform = platform.downcase
69
+ raise 'Platform must be ios or osx' unless %w(ios osx).include?(platform)
70
+ @platform = platform
71
+ end
72
+
73
+ def sdk
74
+ case platform.to_s
75
+ when 'ios'
76
+ 'iphonesimulator'
77
+ when 'osx'
78
+ 'macosx'
79
+ end
80
+ end
81
+
82
+ def allows_reset?
83
+ allow_reset == true
84
+ end
85
+
86
+ def convert_emoji?
87
+ convert_emoji == true
88
+ end
89
+
90
+ def save(destination_path)
91
+ destination_path = Pathname.new(destination_path).expand_path
92
+
93
+ validate! destination_path
94
+
95
+ insert_highlighting_stylesheet
96
+
97
+ # Create playground in a temporary directory before moving in place of
98
+ # any existing playground. This avoids any partially written playground
99
+ # files being re-loaded by Xcode:
100
+ temp_path = Pathname.new(Dir.mktmpdir)
101
+ write_stylesheets(temp_path)
102
+ write_javascripts(temp_path)
103
+ write_sections(temp_path)
104
+ write_xcplayground(temp_path)
105
+
106
+ FileUtils.rm_rf(destination_path) if destination_path.exist?
107
+ FileUtils.mv(temp_path, destination_path)
108
+ ensure
109
+ remove_highlighting_stylesheet
110
+ FileUtils.remove_entry_secure(temp_path) if temp_path && temp_path.exist?
111
+ end
112
+
113
+ private
114
+
115
+ def validate!(destination_path)
116
+ unless destination_path.extname == '.playground'
117
+ raise "Destination path '#{destination_path} does not end in .playground."
118
+ end
119
+
120
+ unless sections.detect { |section| section.is_a?(CodeSection) }
121
+ raise 'A playground must have at least one code section.'
122
+ end
123
+ end
124
+
125
+ def insert_highlighting_stylesheet
126
+ if syntax_highlighting && Util::SyntaxHighlighting.available?
127
+ style = if syntax_highlighting == true
128
+ 'default'
129
+ else
130
+ syntax_highlighting
131
+ end
132
+
133
+ unless style == 'custom'
134
+ @highlighting_css = Stylesheet.new(Util::SyntaxHighlighting.css(style),
135
+ filename: 'highlighting')
136
+ self.stylesheets.insert(0, @highlighting_css)
137
+ end
138
+ end
139
+ end
140
+
141
+ def remove_highlighting_stylesheet
142
+ if @highlighting_css
143
+ self.stylesheets.delete(@highlighting_css)
144
+ @highlighting_css = nil
145
+ end
146
+ end
147
+
148
+ def write_stylesheets(temp_path)
149
+ stylesheets_path = temp_path.join('Documentation')
150
+
151
+ stylesheets.each_with_index do |stylesheet, index|
152
+ number = index + 1
153
+ stylesheet.save(stylesheets_path, number)
154
+ end
155
+ end
156
+
157
+ def write_javascripts(temp_path)
158
+ javascripts_path = temp_path.join('Documentation')
159
+
160
+ javascripts.each_with_index do |javascript, index|
161
+ number = index + 1
162
+ javascript.save(javascripts_path, number)
163
+ end
164
+ end
165
+
166
+ def write_sections(temp_path)
167
+ sections.each_with_index do |section, index|
168
+ number = index + 1
169
+ path = temp_path.join(section.path number)
170
+
171
+ FileUtils.mkdir_p path.dirname
172
+ path.open('w') do |file|
173
+ file.write section.render(number, self)
174
+ end
175
+ end
176
+ end
177
+
178
+ def write_xcplayground(temp_path)
179
+ temp_path.join('contents.xcplayground').open('w') do |file|
180
+ context = TemplateContext.context(self)
181
+ file.write xcplayground_template.result(context)
182
+ end
183
+ end
184
+
185
+ def template_path(*filenames)
186
+ self.class.send(:template_path, *filenames)
187
+ end
188
+
189
+ def xcplayground_template
190
+ self.class.send(:xcplayground_template)
191
+ end
192
+ end
193
+ end