juicer 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/History.txt +10 -0
  2. data/Manifest.txt +58 -0
  3. data/Rakefile +44 -0
  4. data/Readme.rdoc +143 -0
  5. data/bin/juicer +8 -0
  6. data/lib/juicer.rb +70 -0
  7. data/lib/juicer/binary.rb +173 -0
  8. data/lib/juicer/cache_buster.rb +45 -0
  9. data/lib/juicer/chainable.rb +106 -0
  10. data/lib/juicer/cli.rb +56 -0
  11. data/lib/juicer/command/install.rb +59 -0
  12. data/lib/juicer/command/list.rb +50 -0
  13. data/lib/juicer/command/merge.rb +185 -0
  14. data/lib/juicer/command/util.rb +32 -0
  15. data/lib/juicer/command/verify.rb +60 -0
  16. data/lib/juicer/core.rb +59 -0
  17. data/lib/juicer/css_cache_buster.rb +99 -0
  18. data/lib/juicer/install/base.rb +186 -0
  19. data/lib/juicer/install/jslint_installer.rb +51 -0
  20. data/lib/juicer/install/rhino_installer.rb +52 -0
  21. data/lib/juicer/install/yui_compressor_installer.rb +66 -0
  22. data/lib/juicer/jslint.rb +90 -0
  23. data/lib/juicer/merger/base.rb +74 -0
  24. data/lib/juicer/merger/css_dependency_resolver.rb +25 -0
  25. data/lib/juicer/merger/dependency_resolver.rb +82 -0
  26. data/lib/juicer/merger/javascript_dependency_resolver.rb +21 -0
  27. data/lib/juicer/merger/javascript_merger.rb +30 -0
  28. data/lib/juicer/merger/stylesheet_merger.rb +112 -0
  29. data/lib/juicer/minifyer/yui_compressor.rb +129 -0
  30. data/tasks/ann.rake +80 -0
  31. data/tasks/bones.rake +20 -0
  32. data/tasks/gem.rake +201 -0
  33. data/tasks/git.rake +40 -0
  34. data/tasks/notes.rake +27 -0
  35. data/tasks/post_load.rake +34 -0
  36. data/tasks/rdoc.rake +50 -0
  37. data/tasks/rubyforge.rake +55 -0
  38. data/tasks/setup.rb +300 -0
  39. data/tasks/spec.rake +54 -0
  40. data/tasks/svn.rake +47 -0
  41. data/tasks/test.rake +40 -0
  42. data/tasks/test/setup.rake +35 -0
  43. data/test/bin/jslint.js +474 -0
  44. data/test/bin/rhino1_7R1.zip +0 -0
  45. data/test/bin/rhino1_7R2-RC1.zip +0 -0
  46. data/test/bin/yuicompressor +238 -0
  47. data/test/bin/yuicompressor-2.3.5.zip +0 -0
  48. data/test/bin/yuicompressor-2.4.2.zip +0 -0
  49. data/test/juicer/command/test_install.rb +53 -0
  50. data/test/juicer/command/test_list.rb +69 -0
  51. data/test/juicer/command/test_merge.rb +155 -0
  52. data/test/juicer/command/test_util.rb +54 -0
  53. data/test/juicer/command/test_verify.rb +33 -0
  54. data/test/juicer/install/test_installer_base.rb +195 -0
  55. data/test/juicer/install/test_jslint_installer.rb +54 -0
  56. data/test/juicer/install/test_rhino_installer.rb +57 -0
  57. data/test/juicer/install/test_yui_compressor_installer.rb +56 -0
  58. data/test/juicer/merger/test_base.rb +122 -0
  59. data/test/juicer/merger/test_css_dependency_resolver.rb +36 -0
  60. data/test/juicer/merger/test_javascript_dependency_resolver.rb +39 -0
  61. data/test/juicer/merger/test_javascript_merger.rb +74 -0
  62. data/test/juicer/merger/test_stylesheet_merger.rb +178 -0
  63. data/test/juicer/minifyer/test_yui_compressor.rb +159 -0
  64. data/test/juicer/test_cache_buster.rb +58 -0
  65. data/test/juicer/test_chainable.rb +94 -0
  66. data/test/juicer/test_core.rb +47 -0
  67. data/test/juicer/test_css_cache_buster.rb +72 -0
  68. data/test/juicer/test_jslint.rb +33 -0
  69. data/test/test_helper.rb +146 -0
  70. data/test/test_juicer.rb +4 -0
  71. metadata +194 -0
@@ -0,0 +1,10 @@
1
+ == 0.2.0 / 2009-xx-xx
2
+
3
+ * Refactored the minifyers execute method from compress to save
4
+ * Refactored Juicer::Merger::FileMerger -> Juicer::Merger::Base
5
+ * Refactored the mergers and minifyers to be chainable commands.
6
+ * Added Chainable module
7
+
8
+ == 0.1.0 / 2008-12-17
9
+
10
+ * Dug up old project and set it up with Mr Bones
@@ -0,0 +1,58 @@
1
+ History.txt
2
+ Manifest.txt
3
+ Rakefile
4
+ Readme.rdoc
5
+ bin/juicer
6
+ lib/juicer.rb
7
+ lib/juicer/binary.rb
8
+ lib/juicer/cache_buster.rb
9
+ lib/juicer/chainable.rb
10
+ lib/juicer/cli.rb
11
+ lib/juicer/command/install.rb
12
+ lib/juicer/command/list.rb
13
+ lib/juicer/command/merge.rb
14
+ lib/juicer/command/util.rb
15
+ lib/juicer/command/verify.rb
16
+ lib/juicer/core.rb
17
+ lib/juicer/css_cache_buster.rb
18
+ lib/juicer/install/base.rb
19
+ lib/juicer/install/jslint_installer.rb
20
+ lib/juicer/install/rhino_installer.rb
21
+ lib/juicer/install/yui_compressor_installer.rb
22
+ lib/juicer/jslint.rb
23
+ lib/juicer/merger/base.rb
24
+ lib/juicer/merger/css_dependency_resolver.rb
25
+ lib/juicer/merger/dependency_resolver.rb
26
+ lib/juicer/merger/javascript_dependency_resolver.rb
27
+ lib/juicer/merger/javascript_merger.rb
28
+ lib/juicer/merger/stylesheet_merger.rb
29
+ lib/juicer/minifyer/yui_compressor.rb
30
+ tasks/test/setup.rake
31
+ test/bin/jslint.js
32
+ test/bin/rhino1_7R1.zip
33
+ test/bin/rhino1_7R2-RC1.zip
34
+ test/bin/yuicompressor
35
+ test/bin/yuicompressor-2.3.5.zip
36
+ test/bin/yuicompressor-2.4.2.zip
37
+ test/juicer/command/test_install.rb
38
+ test/juicer/command/test_list.rb
39
+ test/juicer/command/test_merge.rb
40
+ test/juicer/command/test_util.rb
41
+ test/juicer/command/test_verify.rb
42
+ test/juicer/install/test_installer_base.rb
43
+ test/juicer/install/test_jslint_installer.rb
44
+ test/juicer/install/test_rhino_installer.rb
45
+ test/juicer/install/test_yui_compressor_installer.rb
46
+ test/juicer/merger/test_base.rb
47
+ test/juicer/merger/test_css_dependency_resolver.rb
48
+ test/juicer/merger/test_javascript_dependency_resolver.rb
49
+ test/juicer/merger/test_javascript_merger.rb
50
+ test/juicer/merger/test_stylesheet_merger.rb
51
+ test/juicer/minifyer/test_yui_compressor.rb
52
+ test/juicer/test_cache_buster.rb
53
+ test/juicer/test_chainable.rb
54
+ test/juicer/test_core.rb
55
+ test/juicer/test_css_cache_buster.rb
56
+ test/juicer/test_jslint.rb
57
+ test/test_helper.rb
58
+ test/test_juicer.rb
@@ -0,0 +1,44 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ load 'tasks/test/setup.rake'
8
+ Bones.setup
9
+ rescue LoadError
10
+ load 'tasks/setup.rb'
11
+ end
12
+
13
+ ensure_in_path 'lib'
14
+ require 'juicer'
15
+
16
+ task :default => 'test:run'
17
+
18
+ PROJ.name = 'juicer'
19
+ PROJ.authors = 'Christian Johansen'
20
+ PROJ.email = 'christian@cjohansen.no'
21
+ PROJ.url = 'http://www.cjohansen.no/en/projects/juicer'
22
+ PROJ.version = Juicer::VERSION
23
+ PROJ.rubyforge.name = 'juicer'
24
+ PROJ.readme_file = 'Readme.rdoc'
25
+ PROJ.exclude = %w(tmp$ bak$ ~$ CVS \.svn ^pkg ^doc \.git ^rcov ^test\/data gemspec ^test\/bin$)
26
+ PROJ.rdoc.remote_dir = 'juicer'
27
+
28
+ PROJ.spec.opts << '--color'
29
+
30
+ PROJ.gem.extras[:post_install_message] = <<-MSG
31
+ Juicer does not ship with third party libraries. You probably want to install
32
+ Yui Compressor and JsLint now:
33
+
34
+ juicer install yui_compressor
35
+ juicer install jslint
36
+
37
+ Happy juicing!
38
+ MSG
39
+
40
+ CLOBBER.include "test/data"
41
+
42
+ depend_on 'cmdparse'
43
+ depend_on 'hpricot'
44
+ depend_on 'rubyzip'
@@ -0,0 +1,143 @@
1
+ = Juicer
2
+ Official URL: http://github.com/cjohansen/juicer/tree/master
3
+ Christian Johansen (http://www.cjohansen.no)
4
+
5
+ == DESCRIPTION:
6
+
7
+ Juicer is a command line tool that helps you ship frontend code for production.
8
+
9
+ High level overview; Juicer can
10
+
11
+ * figure out which files depend on each other and merge them together, reducing
12
+ the number of http requests per page view, thus improving performance
13
+ * use YUI Compressor to compress code, thus improving performance
14
+ * verify that your JavaScript is safe to minify/compress by running JsLint on it
15
+ * cycle asset hosts in CSS files
16
+ * add "cache busters" to URLs in CSS files
17
+ * recalculate relative URLs in CSS files, as well as convert them to absolute
18
+ (or convert absolute URLs to relative URLs)
19
+
20
+ == FEATURES:
21
+
22
+ === Merging and minifying
23
+
24
+ Juicer can read @import statements in CSS files and use them to combine all your
25
+ stylesheets into a single file. This file may be minified using the YUI
26
+ Compressor. Eventually it will support other minifying tools too.
27
+
28
+ Juicer can treat your JavaScript files much the same way too, parsing a comment
29
+ switch @depend, as this example shows:
30
+
31
+ /**
32
+ * My script file
33
+ *
34
+ * @depend jquery-1.2.0.js
35
+ */
36
+ var myNS = {
37
+ myObject = {}
38
+ };
39
+
40
+ Running <tt>juicer merge</tt> on this file will result in a minified file
41
+ <tt>filename.min.js</tt> containing the file jquery-1.2.0.js (located in the
42
+ same directory) and the code above.
43
+
44
+ You can use @import (CSS files) and @depend (JavaScript) recursively, effectively
45
+ creating a dependency chain for Juicer to climb and merge.
46
+
47
+ === Paths
48
+
49
+ When merging CSS files, you may want to merge CSS files in different directories.
50
+ You may also want the resulting CSS file to end up in another directory as well.
51
+ Juicer automatically recalculates referenced URLs to reflect this change.
52
+
53
+ Absolute URLs are not changed by default, but if you provide juicer merge with
54
+ --document-root [DIR] and --relative-urls then absolute URLs are converted to
55
+ URLs relative to output directory. You can also use --absolute-urls to convert
56
+ all URLs to absolute ones.
57
+
58
+ === Cache busters
59
+
60
+ Juicer supports so-called cache busters. A cache buster is a pattern in a path
61
+ whose only purpose is to "trick" browsers to redownload a file when it has
62
+ changed in cases where a far future expires header is used.
63
+
64
+ There are two types of cache busters; soft ones add a parameter to the URL, like
65
+ so: http://assets/images/1.png?cb1234567890, ie the letters "cb" (as in cache
66
+ buster) and then the timestamp of the files mtime.
67
+
68
+ Unfortunately, the popular web proxy Squid shipped for some time with a default
69
+ configuration which would not treat a URL as a "new" URL if the only thing changed
70
+ was the GET parameters. For this reason Juicer provides hard cache busters.
71
+
72
+ Hard cache busters result in URLs such as http://assets/images/1-cb1234567890.png,
73
+ ie URLs that require either renaming of files, or (more conveniently) a web
74
+ server configuration that will forward URLs to the right files anyway.
75
+
76
+ == PLANNED FEATURES:
77
+
78
+ Juicer 0.2.0 is the first usable release. Work will continue from here. Improving
79
+ documentation and APIs is one concern, but there are also new features planned:
80
+
81
+ * Support more minifiers, JsMin (Ruby port), Packer and possibly others
82
+ * Add support for CssTidy to compress CSS files
83
+ * juicer build, a command that can build several files in one swoop using a
84
+ configuration file
85
+ * juicer doc, a command that produces documentation using YUI Doc or JsDoc
86
+
87
+ If you have any ideas, feature requests, want to contribute or whatever, fork
88
+ the project on github, or get in touch through christian (at) cjohansen.no.
89
+
90
+ == SYNOPSIS:
91
+
92
+ juicer merge myfile.css
93
+ -> Produces myfile.min.css which may contain several CSS files, minified
94
+
95
+ juicer merge myfile.js
96
+ -> Produces myfile.min.js, minified and combined
97
+
98
+ juicer help
99
+
100
+ == REQUIREMENTS:
101
+
102
+ In order to use YUI Compressor and JsMin (requires Rhino) you need Java
103
+ installed and the java executable available on your path.
104
+
105
+ == INSTALL:
106
+
107
+ $ gem install juicer
108
+ $ juicer install yui_compressor
109
+
110
+ You need Java installed and available on your PATH. During gem installation,
111
+ Juicer will download and install YUI Compressor, JsLint and Rhino for you.
112
+
113
+ == For developers
114
+
115
+ Before running tests you should run
116
+ rake test:setup
117
+
118
+ which brings in third party libraries (ie, requires a working network connection)
119
+
120
+ == LICENSE:
121
+
122
+ (The MIT License)
123
+
124
+ Copyright (c) 2008-2009 Christian Johansen
125
+
126
+ Permission is hereby granted, free of charge, to any person obtaining
127
+ a copy of this software and associated documentation files (the
128
+ 'Software'), to deal in the Software without restriction, including
129
+ without limitation the rights to use, copy, modify, merge, publish,
130
+ distribute, sublicense, and/or sell copies of the Software, and to
131
+ permit persons to whom the Software is furnished to do so, subject to
132
+ the following conditions:
133
+
134
+ The above copyright notice and this permission notice shall be
135
+ included in all copies or substantial portions of the Software.
136
+
137
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
138
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
139
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
140
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
141
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
142
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
143
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ dir = File.dirname(File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__)
4
+ base = File.expand_path(File.join(dir, %w[.. lib juicer]))
5
+ require base
6
+ require File.join(base, "cli")
7
+
8
+ Juicer::Cli.run(ARGV)
@@ -0,0 +1,70 @@
1
+ require "logger"
2
+
3
+ module Juicer
4
+
5
+ # :stopdoc:
6
+ VERSION = '0.2.0'
7
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
8
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
9
+ LOGGER = Logger.new(STDOUT)
10
+ @@home = nil
11
+ # :startdoc:
12
+
13
+ # Returns the version string for the library.
14
+ #
15
+ def self.version
16
+ VERSION
17
+ end
18
+
19
+ # Returns the installation directory for Juicer
20
+ #
21
+ def self.home
22
+ return @@home if @@home
23
+ return ENV['JUICER_HOME'] if ENV['JUICER_HOME']
24
+ return File.join(ENV['HOME'], ".juicer") if ENV['HOME']
25
+ return File.join(ENV['APPDATA'], "juicer") if ENV['APPDATA']
26
+ return File.join(ENV['HOMEDRIVE'], ENV['HOMEPATH'], "juicer") if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
27
+ return File.join(ENV['USERPROFILE'], "juicer") if ENV['USERPROFILE']
28
+ return File.join(ENV['Personal'], "juicer") if ENV['Personal']
29
+ end
30
+
31
+ # Set home directory
32
+ #
33
+ def self.home=(home)
34
+ @@home = home
35
+ end
36
+
37
+ # Returns the library path for the module. If any arguments are given,
38
+ # they will be joined to the end of the libray path using
39
+ # <tt>File.join</tt>.
40
+ #
41
+ def self.libpath( *args )
42
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
43
+ end
44
+
45
+ # Returns the lpath for the module. If any arguments are given,
46
+ # they will be joined to the end of the path using
47
+ # <tt>File.join</tt>.
48
+ #
49
+ def self.path( *args )
50
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
51
+ end
52
+
53
+ # Utility method used to require all files ending in .rb that lie in the
54
+ # directory below this file that has the same name as the filename passed
55
+ # in. Optionally, a specific _directory_ name can be passed in such that
56
+ # the _filename_ does not have to be equivalent to the directory.
57
+ #
58
+ def self.require_all_libs_relative_to( fname, dir = nil )
59
+ dir ||= ::File.basename(fname, '.*')
60
+ search_me = ::File.expand_path(::File.join(::File.dirname(fname), dir, '**', '*.rb'))
61
+
62
+ Dir.glob(search_me).sort.each { |rb| require rb }
63
+ end
64
+
65
+ end
66
+
67
+ Juicer.require_all_libs_relative_to(__FILE__)
68
+
69
+ class FileNotFoundError < Exception
70
+ end
@@ -0,0 +1,173 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "chainable"))
2
+
3
+ module Juicer
4
+
5
+ # Defines an abstract implementation of a binary that needs to be "shelled
6
+ # out" to be run. Provides a starting point when wrapping and API around a
7
+ # shell binary.
8
+ #
9
+ # The module requires the including class to define the default_options
10
+ # method. It should return a hash of options where options are keys and
11
+ # default values are the values. Only options defined in this hash will be
12
+ # allowed to set on the binary.
13
+ #
14
+ module Binary
15
+
16
+ # Initialize binary with options
17
+ # options = Hash of options, optional
18
+ #
19
+ def initialize(binary, options = {})
20
+ @options = self.respond_to?(:defualt_options) ? default_options.merge(options) : options
21
+ @opt_set = false
22
+ @command = nil
23
+ @binary = binary
24
+ @path = []
25
+ end
26
+
27
+ def path
28
+ @path
29
+ end
30
+
31
+ # Run command
32
+ #
33
+ def execute(params = nil)
34
+ #puts "#{self.command} #{params}"
35
+ cmd = IO.popen("#{self.command} #{params}", "r")
36
+ results = cmd.gets(nil)
37
+ cmd.close
38
+ results
39
+ end
40
+
41
+ # Return the value of a given option
42
+ # opt = The option to return value for
43
+ #
44
+ def get_opt(opt)
45
+ @options[opt] || nil
46
+ end
47
+
48
+ # Return options as a cli arguments string. Optionally accepts a list of
49
+ # options to exclude from the generated string
50
+ #
51
+ def options(*excludes)
52
+ excludes = excludes.flatten.collect { |exc| exc.to_sym }
53
+ @options.inject("") do |str, opt|
54
+ if opt[1].nil? || excludes.include?(opt[0].to_sym)
55
+ str
56
+ else
57
+ val = opt[1] == true ? '' : opt[1]
58
+ option = opt[0].to_s
59
+ option = (option.length == 1 ? "-" : "--") + option.gsub('_', '-')
60
+ "#{str} #{option} #{val}".strip
61
+ end
62
+ end
63
+ end
64
+
65
+ # Set an option. Important: you can only set options that are predefined by the
66
+ # implementing class
67
+ # opt = The option to set
68
+ # value = The value of the option
69
+ #
70
+ def set_opt(opt, value)
71
+ opt = opt.to_sym
72
+ if @options.key?(opt)
73
+ @options[opt] = value
74
+ @opt_set = true
75
+ else
76
+ msg = "Illegal option '#{opt}', specify one of: #{@options.keys.join(', ')}"
77
+ raise ArgumentError.new(msg)
78
+ end
79
+ end
80
+
81
+ # Performs simple parsing of a string of parameters. All recognized
82
+ # parameters are set, non-existent arguments raise an ArgumentError
83
+ #
84
+ def set_opts(options)
85
+ options = options.split " "
86
+ option = nil
87
+ regex = /^--?([^=]*)(=(.*))?/
88
+
89
+ while word = options.shift
90
+ if word =~ regex
91
+ if option
92
+ set_opt option, true
93
+ end
94
+
95
+ if $3
96
+ set_opt $1, $3
97
+ else
98
+ option = $1
99
+ end
100
+ else
101
+ set_opt option, word
102
+ option = nil
103
+ end
104
+ end
105
+ end
106
+
107
+ # Constructs the command to use
108
+ #
109
+ def command
110
+ return @command if !@opt_set && @command
111
+ @opt_set = false
112
+ @command = "#{@binary} #{options}"
113
+ end
114
+
115
+ # Locate the binary to execute. The binary is searched for in the
116
+ # following places:
117
+ #
118
+ # 1) The paths specified through my_binary.path << "/usr/bin"
119
+ # 2) The path specified by the given environment variable
120
+ # 3) Current working directory
121
+ #
122
+ # The name of the binary may be a glob pattern, resulting in +locate+
123
+ # returning an array of matches. This is useful in cases where the path
124
+ # is expected to store several versions oof a binary in the same directory,
125
+ # like /usr/bin/ruby /usr/bin/ruby1.8 /usr/bin/ruby1.9
126
+ #
127
+ # +locate+ always returns an array, or nil if no binaries where found.
128
+ # The result is always all files matching the given pattern in *one* of
129
+ # the specified paths - ie the first path where the pattern matches
130
+ # something.
131
+ #
132
+ def locate(bin_glob, env = nil)
133
+ path << ENV[env] if env && ENV.key?(env) && File.exist?(ENV[env])
134
+
135
+ (path << Dir.pwd).each do |path|
136
+ files = Dir.glob(File.expand_path(File.join(path, bin_glob)))
137
+ return files unless files.empty?
138
+ end
139
+
140
+ nil
141
+ end
142
+
143
+ # Allows for options to be set and read directly on the object as though they were
144
+ # standard attributes. compressor.verbose translates to
145
+ # compressor.get_opt('verbose') and compressor.verbose = true to
146
+ # compressor.set_opt('verbose', true)
147
+ def method_missing(m, *args)
148
+ if @options.key?(m)
149
+ # Only hit method_missing once per option
150
+ self.class.send(:define_method, m) do # def verbose
151
+ get_opt(m) # get_opt(:verbose)
152
+ end # end
153
+
154
+ return get_opt(m)
155
+ end
156
+
157
+ return super unless m.to_s =~ /=$/
158
+
159
+ opt = m.to_s.sub(/=$/, "").to_sym
160
+
161
+ if @options.key?(opt)
162
+ # Only hit method_missing once per option
163
+ self.class.send(:define_method, m) do # def verbose=(val)
164
+ set_opt(opt, args[0]) # set_opt(:verbose, val)
165
+ end # end
166
+
167
+ return set_opt(opt, args[0])
168
+ end
169
+
170
+ super
171
+ end
172
+ end
173
+ end