tay 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +135 -0
  5. data/Rakefile +6 -0
  6. data/bin/tay +15 -0
  7. data/lib/tay.rb +19 -0
  8. data/lib/tay/builder.rb +173 -0
  9. data/lib/tay/cli.rb +29 -0
  10. data/lib/tay/cli/build.rb +15 -0
  11. data/lib/tay/cli/generate.rb +73 -0
  12. data/lib/tay/cli/generators/browser_action.rb +20 -0
  13. data/lib/tay/cli/generators/content_script.rb +37 -0
  14. data/lib/tay/cli/generators/page_action.rb +19 -0
  15. data/lib/tay/cli/generators/templates/browser_action/action.css +4 -0
  16. data/lib/tay/cli/generators/templates/browser_action/action.html +11 -0
  17. data/lib/tay/cli/generators/templates/browser_action/action.js +5 -0
  18. data/lib/tay/cli/generators/templates/browser_action/tayfile +7 -0
  19. data/lib/tay/cli/generators/templates/content_script/content_script.css +6 -0
  20. data/lib/tay/cli/generators/templates/content_script/content_script.js +4 -0
  21. data/lib/tay/cli/generators/templates/content_script/tayfile +6 -0
  22. data/lib/tay/cli/generators/templates/page_action/controller.js +7 -0
  23. data/lib/tay/cli/generators/templates/page_action/icon.png +0 -0
  24. data/lib/tay/cli/generators/templates/page_action/tayfile +7 -0
  25. data/lib/tay/cli/helpers.rb +51 -0
  26. data/lib/tay/cli/minify.rb +44 -0
  27. data/lib/tay/cli/new.rb +37 -0
  28. data/lib/tay/cli/package.rb +41 -0
  29. data/lib/tay/cli/templates/Gemfile +30 -0
  30. data/lib/tay/cli/templates/Tayfile +22 -0
  31. data/lib/tay/cli/templates/gitignore +5 -0
  32. data/lib/tay/cli/validate.rb +21 -0
  33. data/lib/tay/cli/watch.rb +29 -0
  34. data/lib/tay/manifest_generator.rb +160 -0
  35. data/lib/tay/packager.rb +64 -0
  36. data/lib/tay/specification.rb +261 -0
  37. data/lib/tay/specification/action.rb +18 -0
  38. data/lib/tay/specification/browser_action.rb +10 -0
  39. data/lib/tay/specification/content_script.rb +41 -0
  40. data/lib/tay/specification/nacl_module.rb +18 -0
  41. data/lib/tay/specification/packaged_app.rb +33 -0
  42. data/lib/tay/specification/page_action.rb +10 -0
  43. data/lib/tay/specification/web_intent.rb +40 -0
  44. data/lib/tay/specification_validator.rb +167 -0
  45. data/lib/tay/utils.rb +21 -0
  46. data/lib/tay/version.rb +3 -0
  47. data/spec/spec_helper.rb +4 -0
  48. data/spec/tay_spec.rb +5 -0
  49. data/tay.gemspec +26 -0
  50. metadata +215 -0
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ *.DS_Store
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tay.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Thomas Rix
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.
@@ -0,0 +1,135 @@
1
+ # Tay
2
+
3
+ Tay is designed to help you swiftly write Google Chrome extensions using your favourite languages and tools.
4
+
5
+ ## Installation
6
+
7
+ $ gem install tay
8
+
9
+ ## Usage
10
+
11
+ Tay helps create, bootstrap, develop and publish Chrome extentions. It all works through the `tay` command. Running `tay` alone will show you a list of commands.
12
+
13
+ ### Creating
14
+
15
+ To start work on a new extension, run `tay new [name]`. This will create a new directory, `[name]`, containing a predefined directory structure and files.
16
+
17
+ Take particular note of the Gemfile, it contains several commented out gems which can unlock extra abilities for Tay. Just be sure to run `bundle install` after changes there.
18
+
19
+ Usage:
20
+ tay new NAME
21
+
22
+ Options:
23
+ [--no-gitignore=Don't create a .gitignore file]
24
+ [--no-gemfile=Don't create a Gemfile file]
25
+ [--use-coffeescript=Use CoffeeScript gem]
26
+ [--use-haml=Use haml gem]
27
+
28
+ Create a new extension
29
+
30
+ ### Developing
31
+
32
+ Tay helps you use languages and techniques like CoffeeScript, CommonJS, SCSS and HAML. As mentioned above, tay generates a basic extension for you, from that point you can use generators to create various code skeletons.
33
+
34
+ The heart any tay project is the `Tayfile`. It defines metadata about your project and is also used to generate the `manifest.json` that Chrome requires. Some of the basic values will be pre-populated for you. For a full rundown of the Tayfile format, see the docs.
35
+
36
+ If you install one of the JavaScript templating gems that Sprockets supports, you can require them like any other file and they'll be included in the `window.JST` object.
37
+
38
+ #### Generators
39
+
40
+ Tay ships with a handful of code skeleton generators, documented below. Running `tay generate` will show a list of generators; `tay generate help *generator_name*` will show information specific to that generator.
41
+
42
+ Currently available generators: page_action, browser_action, content_script.
43
+
44
+ #### Validating
45
+
46
+ Tay can validate extensions. By running `tay validate` it will check for mutually-exclusive features and missing fields in specifications as well as ensure that referenced files actually exist.
47
+
48
+ Usage:
49
+ tay validate
50
+
51
+ Options:
52
+ [--tayfile=Use the specified tayfile instead of Tayfile]
53
+ -b, [--build-directory=The directory containing the built extension]
54
+ # Default: build
55
+
56
+ Validate the current extension
57
+
58
+ #### Building
59
+
60
+ To build the extension, run `tay build`. Source files will be compiled and written in to the build subdirectory by default. At this point, the unpacked extension can be loaded in to the browser by following the relevant section in Chrome's [getting started](http://code.google.com/chrome/extensions/getstarted.html) tutorial.
61
+
62
+ Usage:
63
+ tay build
64
+
65
+ Options:
66
+ [--tayfile=Use the specified tayfile instead of Tayfile]
67
+ -b, [--build-directory=The directory to build in]
68
+ # Default: build
69
+
70
+ Build the current extension
71
+
72
+ If you run `tay watch` in the top level directory, tay will automatically build whenever a change is detected in the `src` directory *(you still need to go and reload the unpacked extension in Chrome)*.
73
+
74
+ To use `tay watch` you'll need to add the `[guard-tay](http://github.com/rixth/guard-tay` gem to your Gemfile and run `bundle install`. If you want to customize the directories that are watched for changes, you can generate a [Guardfile](http://github.com/guard/guard) by running `guard init tay`.
75
+
76
+ Usage:
77
+ tay watch
78
+
79
+ Options:
80
+ [--tayfile=Use the specified tayfile instead of Tayfile]
81
+ -b, [--build-directory=The directory to build in]
82
+ # Default: build
83
+
84
+ Watch the current extension and recompile on file change
85
+
86
+ ### Minifying
87
+
88
+ If you have the `[uglifier](https://github.com/lautis/uglifier)` and/or `[yui-compressor](https://github.com/sstephenson/ruby-yui-compressor)` gems in your Gemfile, you will be able to minify the JS and CSS in a built extension.
89
+
90
+ Usage:
91
+ tay minify
92
+
93
+ Options:
94
+ [--tayfile=Use the specified tayfile instead of Tayfile]
95
+ -b, [--build-directory=The directory containing the built extension]
96
+ # Default: build
97
+ [--skip-js=Don't minify *.js files]
98
+ [--skip-css=Don't minify *.css files]
99
+
100
+ Minify the CSS and JS of the currently built extension
101
+
102
+ ### Packaging
103
+
104
+ When it comes time to publish your extension, Tay can create both .zip files (default, for submission to the Chrome Web Store) and .crx files (for self-hosting) via the `tay package` command.
105
+
106
+ There are [certain requirements](http://code.google.com/chrome/extensions/packaging.html) with regards to private keys to allow updates to your extension without changing its ID. Tay will generate a key and place it in the project root. It's up to you whether this gets checked in to version control (not recommended for public GitHub projects). If a key is already present when the packager is run, it will be used.
107
+
108
+ The filename of the key can be specified using by setting `key_path` in the Tayfile.
109
+
110
+ Usage:
111
+ tay package
112
+
113
+ Options:
114
+ [--tayfile=Use the specified tayfile instead of Tayfile]
115
+ -b, [--build-directory=The directory containing the built extension]
116
+ # Default: build
117
+ -t, [--type=The file type to build, zip or crx]
118
+ # Default: zip
119
+
120
+ Package the current extension
121
+
122
+
123
+ ## Contributing
124
+
125
+ 1. Fork it
126
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
127
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
128
+ 4. Push to the branch (`git push origin my-new-feature`)
129
+ 5. Create new Pull Request
130
+
131
+ ## TODO
132
+
133
+ * Generators that can also make haml/coffeescript
134
+ * Localization of extensions
135
+ * Generator for extension option pages
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ task :doc do
5
+ `sdoc`
6
+ end
data/bin/tay ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+ require 'tay'
5
+ Bundler.require if File.exists?('Gemfile')
6
+
7
+ begin
8
+ require 'tay/cli'
9
+ Tay::CLI::Root.start
10
+ rescue Interrupt => e
11
+ puts "\nQuitting..."
12
+ exit 1
13
+ rescue SystemExit => e
14
+ exit e.status
15
+ end
@@ -0,0 +1,19 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'fileutils'
4
+ require 'pathname'
5
+ require 'json'
6
+
7
+ require 'tay/version'
8
+ require 'tay/utils'
9
+ require 'tay/specification'
10
+ require 'tay/specification_validator'
11
+ require 'tay/manifest_generator'
12
+ require 'tay/builder'
13
+ require 'tay/packager'
14
+
15
+ module Tay
16
+ class InvalidSpecification < Exception; end
17
+ class SpecificationNotFound < Exception; end
18
+ class InvalidPackageType < Exception; end
19
+ end
@@ -0,0 +1,173 @@
1
+ require 'tilt'
2
+ require 'sprockets'
3
+
4
+ module Tay
5
+ ##
6
+ # Takes a Tay::Specification and builds it. It compiles the assets,
7
+ # writes the manifest, and copies everything to the output path.
8
+ class Builder
9
+ ##
10
+ # Pointer to the relevant Tay::Specification
11
+ attr_reader :spec
12
+
13
+ ##
14
+ # Set to true for debug output
15
+ attr_accessor :debug
16
+
17
+ ##
18
+ # Create a new builder. You must pass the specification, full path to the
19
+ # source directory and an optional output directory which defaults to
20
+ # base_dir + '/build'
21
+ def initialize(specification, base_dir, output_dir = nil)
22
+ @spec = specification
23
+ @base_dir = Pathname.new(base_dir)
24
+ @output_dir = output_dir ? Pathname.new(output_dir) : @base_dir.join('build')
25
+ create_sprockets_environment
26
+ end
27
+
28
+ ##
29
+ # Do the building. This simply delegates to the private methods
30
+ # in this class.
31
+ def build!
32
+ create_output_directory
33
+ simple_compile_directory('html')
34
+ simple_compile_directory('assets')
35
+ compile_files(spec.all_javascript_paths)
36
+ compile_files(spec.all_stylesheet_paths)
37
+ write_manifest
38
+ end
39
+
40
+ ##
41
+ # This is sub-optimal but the mustache/js gem does not require its tilt
42
+ # template automatically.
43
+ def self.try_to_load_mustache_trimmer
44
+ begin
45
+ require 'tilt/mustache_js_template'
46
+ Sprockets.register_engine '.mustache', Tilt::MustacheJsTemplate
47
+ rescue LoadError
48
+ end
49
+ end
50
+
51
+ protected
52
+
53
+ ##
54
+ # Given a path, run it through tilt and return the compiled version.
55
+ # If there's no known engine for it, just return the content verbatim.
56
+ # If we know the type buy are missing the gem, raise an exception.
57
+ def get_compiled_file_content(path)
58
+ begin
59
+ Tilt.new(path.to_s).render
60
+ rescue RuntimeError
61
+ File.read(path)
62
+ end
63
+ end
64
+
65
+ ##
66
+ # Create the output directory if it does not exist
67
+ def create_output_directory
68
+ FileUtils.mkdir_p @output_dir
69
+ end
70
+
71
+ ##
72
+ # Copy all the files from a directory to the output, compiling
73
+ # them if they are familiar to us. Does not do any sprocketing.
74
+ def simple_compile_directory(directory)
75
+ Dir[@base_dir.join('src', directory, '**/*')].each do |path|
76
+ file_in_path = Pathname.new(path)
77
+ file_out_path = asset_output_filename(src_path_to_out_path(path), Tilt.mappings.keys)
78
+
79
+ content = get_compiled_file_content(file_in_path)
80
+
81
+ FileUtils.mkdir_p(file_out_path.dirname)
82
+ File.open(file_out_path, 'w') do |f|
83
+ f.write content
84
+ end
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Process all the files in the directory through sprockets before writing
90
+ # them to the output directory
91
+ def compile_files(files)
92
+ files.each do |base_path|
93
+ # We do this second glob in case the path provided in the tayfile
94
+ # references a compiled version
95
+ Dir[@base_dir.join('src', base_path + '*')].each do |path|
96
+ path = Pathname.new(path).relative_path_from(@base_dir.join('src'))
97
+ file_in_path = @base_dir.join('src', path)
98
+ file_out_path = asset_output_filename(@output_dir.join(path), @sprockets.engines.keys)
99
+
100
+ if @sprockets.extensions.include?(path.extname)
101
+ content = @sprockets[file_in_path].to_s
102
+ else
103
+ content = File.read(file_in_path)
104
+ end
105
+
106
+ FileUtils.mkdir_p(file_out_path.dirname)
107
+ File.open(file_out_path, 'w') do |f|
108
+ f.write content
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ ##
115
+ # Generate the manifest from the spec and write it to disk
116
+ def write_manifest
117
+ generator = ManifestGenerator.new(spec)
118
+
119
+ File.open(@output_dir.join('manifest.json'), 'w') do |f|
120
+ f.write JSON.pretty_generate(generator.spec_as_json)
121
+ end
122
+ end
123
+
124
+ ##
125
+ # Set up the sprockets environment for munging all the things
126
+ def create_sprockets_environment
127
+ @sprockets = Sprockets::Environment.new
128
+ @sprockets.append_path(@base_dir.join('src/javascripts').to_s)
129
+ @sprockets.append_path(@base_dir.join('src/templates').to_s)
130
+ @sprockets.append_path(@base_dir.join('src/stylesheets').to_s)
131
+ @sprockets.append_path(@base_dir.join('src').to_s)
132
+ @sprockets.append_path(@base_dir.to_s)
133
+ end
134
+
135
+ ##
136
+ # Debug message helper
137
+ def dbg(msg)
138
+ puts dbg if debug
139
+ end
140
+
141
+ ##
142
+ # Helper function that converts a base_dir/src/XYZ path to the equivalent
143
+ # path in the output directory
144
+ def src_path_to_out_path(path)
145
+ @output_dir.join(path.to_s.sub(/\A#{@base_dir.to_s}\/src\//, ''))
146
+ end
147
+
148
+ ##
149
+ # Helper function to convert the filenames of assets requiring pre-
150
+ # processing to their compiled extension. However, if the file only
151
+ # has one extension, it will be left alone regardless. Examples:
152
+ #
153
+ # * "foobar.module.js.coffee" => "foobar.module.js"
154
+ # * "index.html.haml" => "index.html"
155
+ # * "style.scss" => "style.scss"
156
+ def asset_output_filename(path, processed_extensions)
157
+ path = Pathname.new(path) if path.is_a?(String)
158
+
159
+ return path if path.basename.to_s.split('.').length == 2
160
+
161
+ extension = path.extname
162
+ processed_extensions.map! { |ext| (ext[0] != '.' ? '.' : '') + ext }
163
+
164
+ if processed_extensions.include?(extension)
165
+ asset_output_filename(path.to_s.sub(/#{extension}\Z/, ''), processed_extensions)
166
+ else
167
+ path
168
+ end
169
+ end
170
+ end
171
+
172
+ Builder.try_to_load_mustache_trimmer
173
+ end
@@ -0,0 +1,29 @@
1
+ require 'thor'
2
+ require 'thor/group'
3
+ require 'tay/cli/helpers'
4
+
5
+ module Tay
6
+ ##
7
+ # Runs tay's command line interface
8
+ module CLI
9
+ class Root < ::Thor
10
+ include ::Thor::Actions
11
+ include ::Tay::CLI::Helpers
12
+
13
+ require 'tay/cli/generate'
14
+ register(Tay::CLI::Generate, 'generate', 'generate', 'Generate a feature')
15
+
16
+ require 'tay/cli/new'
17
+ require 'tay/cli/build'
18
+ require 'tay/cli/watch'
19
+ require 'tay/cli/generate'
20
+ require 'tay/cli/validate'
21
+ require 'tay/cli/minify'
22
+ require 'tay/cli/package'
23
+
24
+ def self.source_root
25
+ File.expand_path('cli/templates', File.dirname(__FILE__))
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ module Tay
2
+ module CLI
3
+ class Root < ::Thor
4
+ desc 'build', 'Build the current extension'
5
+ method_option 'tayfile', :type => :string,
6
+ :banner => 'Use the specified tayfile instead of Tayfile'
7
+ method_option 'build-directory', :type => :string, :default => 'build',
8
+ :aliases => '-b', :banner => 'The directory to build in'
9
+ def build
10
+ builder = Builder.new(spec, base_dir, build_dir)
11
+ builder.build!
12
+ end
13
+ end
14
+ end
15
+ end