tay 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,5 @@
1
+ *.DS_Store
2
+ Gemfile.lock
3
+ build/
4
+ pkg/
5
+ tmp/
@@ -0,0 +1,21 @@
1
+ module Tay
2
+ module CLI
3
+ class Root < ::Thor
4
+ desc 'validate', 'Validate 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 containing the built extension'
9
+ def validate
10
+ validator = SpecificationValidator.new(spec, build_dir)
11
+ validator.on_message = lambda do |type, message|
12
+ say(type.upcase + ": " + message, type == 'warn' ? :yellow : :red)
13
+ end
14
+
15
+ if validator.validate!
16
+ say("All OK!", :green)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ module Tay
2
+ module CLI
3
+ class Root < ::Thor
4
+ desc 'watch', 'Watch the current extension and recompile on file change'
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 watch
10
+ begin
11
+ require 'guard'
12
+ require 'guard/tay'
13
+ rescue LoadError
14
+ say('ERROR: please add the guard and guard-tay gems to your Gemfile to enable auto compilation', :red)
15
+ return
16
+ end
17
+
18
+ guardfile_path= "#{::Guard.locate_guard('tay')}/lib/guard/tay/templates/Guardfile"
19
+ guardfile = File.read(guardfile_path)
20
+
21
+ # Proxy in our command line options
22
+ guardfile.sub!(':tay do', ":tay, #{options.to_s} do")
23
+
24
+ Guard.setup
25
+ Guard.start(:guardfile_contents => guardfile)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,160 @@
1
+ module Tay
2
+ ##
3
+ # Takes a Tay::Specification and turns it in to a JSON representation
4
+ # that is eventually saved in to the manifest.json file.
5
+ #
6
+ # It also does basic sanity checks for missing required fields and common
7
+ # mistakes.
8
+ class ManifestGenerator
9
+ ##
10
+ # Pointer to the relevant Tay::Specification
11
+ attr_reader :spec
12
+
13
+ def initialize(specification)
14
+ @spec = specification
15
+ end
16
+
17
+ ##
18
+ # Return the JSON representation of the specification
19
+ def spec_as_json
20
+ json = {
21
+ :name => spec.name,
22
+ :version => spec.version,
23
+ :manifest_version => calculate_manifest_version
24
+ }
25
+
26
+ json[:description] = spec.description if spec.description
27
+ json[:icons] = spec.icons
28
+ json[:default_locale] = spec.default_locale if spec.default_locale
29
+ json[:browser_action] = action_as_json(spec.browser_action) if spec.browser_action
30
+ json[:page_action] = action_as_json(spec.page_action) if spec.page_action
31
+ json[:app] = packaged_app_as_json if spec.packaged_app
32
+ if spec.background_page
33
+ json[:background] = { :page => spec.background_page }
34
+ end
35
+ unless spec.background_scripts.empty?
36
+ json[:background] = { :scripts => spec.background_scripts }
37
+ end
38
+ json[:chrome_url_overrides] = spec.overriden_pages unless spec.overriden_pages.empty?
39
+ json[:content_scripts] = content_scripts_as_json unless spec.content_scripts.empty?
40
+ json[:content_security_policy] = spec.content_security_policy if spec.content_security_policy
41
+ json[:homepage_url] = spec.homepage if spec.homepage
42
+ json[:incognito] = spec.incognito_mode if spec.incognito_mode
43
+ json[:intents] = web_intents_as_json unless spec.web_intents.empty?
44
+ json[:minimum_chrome_version] = spec.minimum_chrome_version if spec.minimum_chrome_version
45
+ json[:nacl_modules] = nacl_modules_as_json unless spec.nacl_modules.empty?
46
+ json[:offline_enabled] = spec.offline_enabled unless spec.offline_enabled.nil?
47
+ json[:omnibox] = spec.omnibox_keyword if spec.omnibox_keyword
48
+ json[:options_page] = spec.options_page if spec.options_page
49
+ json[:permissions] = spec.permissions unless spec.permissions.empty?
50
+ json[:requirements] = requirements_as_json if has_requirements?
51
+ json[:update_url] = spec.update_url if spec.update_url
52
+ json[:web_accessible_resources] = spec.web_accessible_resources unless spec.web_accessible_resources.empty?
53
+
54
+ json
55
+ end
56
+
57
+ protected
58
+
59
+ ##
60
+ # Work out which manifest version needed based on the features that have
61
+ # been used
62
+ def calculate_manifest_version
63
+ return 2 unless spec.web_accessible_resources.empty?
64
+ 1
65
+ end
66
+
67
+ ##
68
+ # Return the manifest representation of a page or browser action
69
+ def action_as_json(action)
70
+ json = {}
71
+ json[:default_title] = action.title if action.title
72
+ json[:default_icon] = action.icon if action.icon
73
+ json[:default_popup] = action.popup if action.popup
74
+ json
75
+ end
76
+
77
+ ##
78
+ # Return the manifest representation of a packaged app
79
+ def packaged_app_as_json
80
+ app = spec.packaged_app
81
+ json = {
82
+ :local_path => app.page
83
+ }
84
+
85
+ unless app.container.nil?
86
+ json[:container] = app.container
87
+ if app.container == 'panel'
88
+ json[:width] = app.width
89
+ json[:height] = app.height
90
+ end
91
+ end
92
+
93
+ json
94
+ end
95
+
96
+ ##
97
+ # Return the manifest representation of the content scripts, if any
98
+ def content_scripts_as_json
99
+ spec.content_scripts.map do |cs|
100
+ cs_json = {
101
+ :matches => cs.include_patterns
102
+ }
103
+
104
+ cs_json[:exclude_matches] = cs.exclude_patterns unless cs.exclude_patterns.empty?
105
+ cs_json[:run_at] = cs.run_at if cs.run_at
106
+ cs_json[:all_frames] = cs.all_frames unless cs.all_frames.nil?
107
+ cs_json[:css] = cs.stylesheets unless cs.stylesheets.empty?
108
+ cs_json[:js] = cs.javascripts unless cs.javascripts.empty?
109
+
110
+ cs_json
111
+ end
112
+ end
113
+
114
+ ##
115
+ # Return the manifest representation of handled web intents, if any
116
+ def web_intents_as_json
117
+ spec.web_intents.map do |wi|
118
+ {
119
+ :action => wi.action,
120
+ :title => wi.title,
121
+ :href => wi.href,
122
+ :types => wi.types,
123
+ :disposition => wi.disposition
124
+ }
125
+ end
126
+ end
127
+
128
+ ##
129
+ # Return the manifest representation of native client modules, if any
130
+ def nacl_modules_as_json
131
+ spec.nacl_modules.map do |nm|
132
+ {
133
+ :path => nm.path,
134
+ :mime_type => nm.mime_type
135
+ }
136
+ end
137
+ end
138
+
139
+ ##
140
+ # Return the manifest representation of any technology requirements
141
+ def requirements_as_json
142
+ features = []
143
+ features << 'webgl' if spec.requires_webgl
144
+ features << 'css3d' if spec.requires_3d_css_transitions
145
+
146
+ {
147
+ '3D' => {
148
+ :features => features
149
+ }
150
+ }
151
+ end
152
+
153
+ ##
154
+ # Calculates if the manifest should have a requirements field
155
+ def has_requirements?
156
+ spec.requires_webgl || spec.requires_3d_css_transitions
157
+ end
158
+
159
+ end
160
+ end
@@ -0,0 +1,64 @@
1
+ require 'crxmake'
2
+
3
+ module Tay
4
+ ##
5
+ # Takes a Tay::Specification and builds it. It compiles the assets,
6
+ # writes the manifest, and copies everything to the output path.
7
+ class Packager
8
+ ##
9
+ # Pointer to the relevant Tay::Specification
10
+ attr_reader :spec
11
+
12
+ ##
13
+ # Create a new builder. You must pass the specification, full path to the
14
+ # source directory and an optional output directory which defaults to
15
+ # base_dir + '/build'
16
+ def initialize(specification, base_dir, build_dir)
17
+ @spec = specification
18
+ @base_dir = Pathname.new(base_dir)
19
+ @build_dir = Pathname.new(build_dir)
20
+ end
21
+
22
+ ##
23
+ # Write a signed zip file to out_path for upload to the Web Store
24
+ def write_zip(out_path)
25
+ CrxMake.zip(
26
+ :ex_dir => @build_dir,
27
+ :pkey => full_key_path,
28
+ :zip_output => out_path,
29
+ :verbose => false
30
+ )
31
+ end
32
+
33
+ ##
34
+ # Write a signed crx file to out_path for self hosting
35
+ def write_crx(out_path)
36
+ CrxMake.make(
37
+ :ex_dir => @build_dir,
38
+ :pkey => full_key_path,
39
+ :crx_output => out_path,
40
+ :verbose => false
41
+ )
42
+ end
43
+
44
+ ##
45
+ # Do we have an existing key file?
46
+ def private_key_exists?
47
+ full_key_path.exist?
48
+ end
49
+
50
+ ##
51
+ # Return the absolute path to the private key
52
+ def full_key_path
53
+ Pathname.new(spec.key_path).expand_path(@base_dir)
54
+ end
55
+
56
+ ##
57
+ # Generate a key with OpenSSL and write it to the key path
58
+ def write_new_key
59
+ File.open(full_key_path, 'w') do |f|
60
+ f.write OpenSSL::PKey::RSA.generate(1024).export()
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,261 @@
1
+ require 'tay/specification/action'
2
+ require 'tay/specification/page_action'
3
+ require 'tay/specification/browser_action'
4
+ require 'tay/specification/packaged_app'
5
+ require 'tay/specification/content_script'
6
+ require 'tay/specification/web_intent'
7
+ require 'tay/specification/nacl_module'
8
+
9
+ module Tay
10
+ ##
11
+ # A tay specification helps you define the functionality that the extension
12
+ # provides. It also serves as the data source that it used to generate the
13
+ # manifest.json file.
14
+ class Specification
15
+ ##
16
+ # http://code.google.com/chrome/extensions/manifest.html#name
17
+ attr_accessor :name
18
+
19
+ ##
20
+ # http://code.google.com/chrome/extensions/manifest.html#version
21
+ attr_accessor :version
22
+
23
+ ##
24
+ # http://code.google.com/chrome/extensions/manifest.html#description
25
+ attr_accessor :description
26
+
27
+ ##
28
+ # http://code.google.com/chrome/extensions/manifest.html#homepage_url
29
+ attr_accessor :homepage
30
+
31
+ ##
32
+ # http://code.google.com/chrome/extensions/manifest.html#default_locale
33
+ attr_accessor :default_locale
34
+
35
+ ##
36
+ # Path to an HTML file that will be used as the background page
37
+ attr_accessor :background_page
38
+
39
+ ##
40
+ # http://code.google.com/chrome/extensions/manifest.html#options_page
41
+ attr_accessor :options_page
42
+
43
+ ##
44
+ # http://code.google.com/chrome/extensions/omnibox.html
45
+ attr_accessor :omnibox_keyword
46
+
47
+ ##
48
+ # http://code.google.com/chrome/extensions/contentSecurityPolicy.html
49
+ attr_accessor :content_security_policy
50
+
51
+ ##
52
+ # http://code.google.com/chrome/extensions/manifest.html#minimum_chrome_version
53
+ attr_accessor :minimum_chrome_version
54
+
55
+ ##
56
+ # http://code.google.com/chrome/extensions/manifest.html#incognito
57
+ attr_accessor :incognito_mode
58
+
59
+ ##
60
+ # http://code.google.com/chrome/extensions/manifest.html#offline_enabled
61
+ attr_accessor :offline_enabled
62
+
63
+ ##
64
+ # Set this to true to mark the extension as requiring webgl support
65
+ attr_accessor :requires_webgl
66
+
67
+ ##
68
+ # Set this to true to mark the extension as requiring 3d css support
69
+ attr_accessor :requires_3d_css_transitions
70
+
71
+ ##
72
+ # http://code.google.com/chrome/extensions/autoupdate.html
73
+ attr_accessor :update_url
74
+
75
+ ##
76
+ # If you set this to a hash, it will be merged on top of the generated
77
+ # manifest. This is useful to force override of specific values
78
+ attr_accessor :manifest_data
79
+
80
+ ##
81
+ # An array of Tay::Specification::NaClModule
82
+ attr_accessor :nacl_modules
83
+
84
+ ##
85
+ # An array of Tay::Specification::ContentScript objects
86
+ attr_accessor :content_scripts
87
+
88
+ ##
89
+ # An array of Tay::Specification::WebIntent objects
90
+ attr_accessor :web_intents
91
+
92
+ ##
93
+ # An array javascript file paths that will run in the background
94
+ #
95
+ # http://code.google.com/chrome/extensions/background_pages.html
96
+ attr_accessor :background_scripts
97
+
98
+ ##
99
+ # An array of paths considered "web accessible"
100
+ #
101
+ # http://code.google.com/chrome/extensions/manifest.html#web_accessible_resources
102
+ attr_accessor :web_accessible_resources
103
+
104
+ ##
105
+ # An array of permissions the extension requires
106
+ #
107
+ # http://code.google.com/chrome/extensions/manifest.html#permissions
108
+ attr_accessor :permissions
109
+
110
+ ##
111
+ # An array of javascript paths that will be copied to the build
112
+ attr_accessor :javascripts
113
+
114
+ ##
115
+ # An array of stylesheet paths that will be copied to the build
116
+ attr_accessor :stylesheets
117
+
118
+ ##
119
+ # An map of icon sizes to paths
120
+ #
121
+ # http://code.google.com/chrome/extensions/manifest.html#icons
122
+ attr_accessor :icons
123
+
124
+ ##
125
+ # A map of Chrome page types to HTML files
126
+ attr_reader :overriden_pages
127
+
128
+ ##
129
+ # A Tay::Specification::BrowserAction or nil
130
+ attr_reader :browser_action
131
+
132
+ ##
133
+ # A Tay::Specification::PageAction or nil
134
+ attr_reader :page_action
135
+
136
+ ##
137
+ # A Tay::Specification::PackagedApp or nil
138
+ attr_reader :packaged_app
139
+
140
+ ##
141
+ # The path to the private key used to package this extension. Will default
142
+ # to ./EXTNAME.pem
143
+ attr_accessor :key_path
144
+
145
+ def initialize(&block)
146
+ @nacl_modules = []
147
+ @overriden_pages = {}
148
+ @web_accessible_resources = []
149
+ @permissions = []
150
+ @background_scripts = []
151
+ @content_scripts = []
152
+ @web_intents = []
153
+ @icons = {}
154
+ @stylesheets = []
155
+ @javascripts = []
156
+
157
+ yield self
158
+ end
159
+
160
+ ##
161
+ # Provide the path of the icon to be used at a certain size
162
+ def add_icon(size, path)
163
+ @icons[size.to_i] = path
164
+ end
165
+
166
+ ##
167
+ # Create a new Tay::Specification::BrowserAction and pass it to the block
168
+ # for set up.
169
+ def add_browser_action(&block)
170
+ raise Tay::InvalidSpecification.new('Browser action already set up') if @browser_action
171
+ @browser_action = BrowserAction.new
172
+ yield @browser_action
173
+ end
174
+
175
+ ##
176
+ # Create a new Tay::Specification::BrowserAction and pass it to the block
177
+ # for set up.
178
+ def add_page_action(&block)
179
+ raise Tay::InvalidSpecification.new('Page action already set up') if @page_action
180
+ @page_action = PageAction.new
181
+ yield @page_action
182
+ end
183
+
184
+ ##
185
+ # Create a new Tay::Specification::BrowserAction and pass it to the block
186
+ # for set up.
187
+ def add_packaged_app(&block)
188
+ raise Tay::InvalidSpecification.new('Packaged app already set up') if @packaged_app
189
+ @packaged_app = PackagedApp.new
190
+ yield @packaged_app
191
+ end
192
+
193
+ ##
194
+ # Add a content script to this extension. It will be passed to the block
195
+ # for set up. See Tay::Specification::ContentScript
196
+ def add_content_script(&block)
197
+ content_script = ContentScript.new
198
+ yield content_script
199
+ @content_scripts << content_script
200
+ end
201
+
202
+ ##
203
+ # Add a web intent to this extension. It will be passed to the block for
204
+ # set up. See Tay::Specification::WebIntent
205
+ def add_web_intent(&block)
206
+ web_intent = WebIntent.new
207
+ yield web_intent
208
+ @web_intents << web_intent
209
+ end
210
+
211
+ ##
212
+ # Add a native client module to this extension. It will be passed to the
213
+ # block for set up. See Tay::Specification::NaClModule
214
+ def add_nacl_module(&block)
215
+ nacl_module = NaClModule.new
216
+ yield nacl_module
217
+ @nacl_modules << nacl_module
218
+ end
219
+
220
+ ##
221
+ # Override a built in Chrome page with your own HTML file
222
+ #
223
+ # http://code.google.com/chrome/extensions/override.html
224
+ def override_page(page_type, path)
225
+ @overriden_pages[page_type] = path
226
+ end
227
+
228
+ ##
229
+ # Return all the javascript paths in the spec, included those nested
230
+ # inside other objects
231
+ def all_javascript_paths
232
+ all_paths = []
233
+ all_paths += @javascripts
234
+ all_paths += @background_scripts
235
+ all_paths += @content_scripts.map { |cs| cs.javascripts }.compact
236
+ all_paths.flatten.uniq
237
+ end
238
+
239
+ ##
240
+ # Return all the stylesheet paths in the spec, included those nested
241
+ # inside other objects
242
+ def all_stylesheet_paths
243
+ all_paths = []
244
+ all_paths += @stylesheets
245
+ all_paths += @content_scripts.map { |cs| cs.stylesheets }.compact
246
+ all_paths.flatten.uniq
247
+ end
248
+
249
+ ##
250
+ # Get the path to the private key for this extension
251
+ def key_path
252
+ @key_path || Utils.filesystem_name(name) + '.pem'
253
+ end
254
+
255
+ ##
256
+ # Does the specification have a certain permission?
257
+ def has_permission?(permission)
258
+ permissions.include?(permission)
259
+ end
260
+ end
261
+ end