jim 0.2.3 → 0.3.0.pre

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 (66) hide show
  1. data/Gemfile +2 -1
  2. data/Gemfile.lock +2 -0
  3. data/HISTORY +13 -0
  4. data/README.md +148 -0
  5. data/Rakefile +6 -3
  6. data/bin/jim +1 -2
  7. data/default +0 -0
  8. data/jim.gemspec +142 -105
  9. data/lib/jim.rb +1 -1
  10. data/lib/jim/bundler.rb +168 -73
  11. data/lib/jim/cli.rb +200 -151
  12. data/lib/jim/index.rb +20 -9
  13. data/lib/jim/installer.rb +46 -46
  14. data/lib/jim/rack.rb +57 -20
  15. data/lib/jim/templates/jimfile +11 -5
  16. data/lib/jim/version_parser.rb +3 -3
  17. data/test/fixtures/infoincomments.js +1 -1
  18. data/test/fixtures/jimfile +14 -7
  19. data/test/fixtures/jquery-1.4.1.js +3 -6057
  20. data/test/fixtures/jquery.color.js +1 -1
  21. data/test/fixtures/localfile.js +1 -1
  22. data/test/fixtures/mustache.js/package.json +1 -1
  23. data/test/fixtures/noversion.js +1 -1
  24. data/test/fixtures/old_jimfile +7 -0
  25. data/test/fixtures/sammy-0.5.0/examples/backend/app.rb +4 -4
  26. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/app.js +18 -18
  27. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/jquery.cloudkit.js +1 -1
  28. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/jquery.js +1 -1
  29. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/sammy.js +161 -161
  30. data/test/fixtures/sammy-0.5.0/examples/backend/public/templates/task.html.erb +1 -1
  31. data/test/fixtures/sammy-0.5.0/examples/backend/public/templates/task_details.html.erb +1 -1
  32. data/test/fixtures/sammy-0.5.0/examples/backend/views/app.sass +6 -6
  33. data/test/fixtures/sammy-0.5.0/examples/backend/views/index.haml +5 -5
  34. data/test/fixtures/sammy-0.5.0/examples/form_handling/index.html +16 -16
  35. data/test/fixtures/sammy-0.5.0/examples/hello_world/index.html +13 -13
  36. data/test/fixtures/sammy-0.5.0/examples/location_override/data.html +28 -28
  37. data/test/fixtures/sammy-0.5.0/examples/location_override/index.html +18 -18
  38. data/test/fixtures/sammy-0.5.0/examples/location_override/test.html +36 -36
  39. data/test/fixtures/sammy-0.5.0/lib/min/sammy-0.5.0.min.js +1 -1
  40. data/test/fixtures/sammy-0.5.0/lib/min/sammy-lastest.min.js +1 -1
  41. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.cache.js +13 -13
  42. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.haml.js +2 -2
  43. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.json.js +15 -15
  44. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.mustache.js +46 -46
  45. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.nested_params.js +29 -29
  46. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.storage.js +54 -54
  47. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.template.js +17 -17
  48. data/test/fixtures/sammy-0.5.0/lib/sammy.js +220 -220
  49. data/test/fixtures/sammy-0.5.0/test/fixtures/partial.html +1 -1
  50. data/test/fixtures/sammy-0.5.0/test/index.html +26 -26
  51. data/test/fixtures/sammy-0.5.0/test/test_sammy_application.js +60 -60
  52. data/test/fixtures/sammy-0.5.0/test/test_sammy_event_context.js +21 -21
  53. data/test/fixtures/sammy-0.5.0/test/test_sammy_location_proxy.js +3 -3
  54. data/test/fixtures/sammy-0.5.0/test/test_sammy_plugins.js +17 -17
  55. data/test/fixtures/sammy-0.5.0/test/test_sammy_storage.js +4 -4
  56. data/test/helper.rb +15 -0
  57. data/test/test_jim_bundler.rb +114 -74
  58. data/test/test_jim_cli.rb +34 -18
  59. data/test/test_jim_index.rb +19 -19
  60. data/test/test_jim_installer.rb +13 -13
  61. data/test/test_jim_rack.rb +41 -0
  62. data/test/test_jim_version_parser.rb +4 -4
  63. metadata +204 -41
  64. data/.gitignore +0 -24
  65. data/README.rdoc +0 -105
  66. data/lib/jim/templates/commands +0 -58
data/lib/jim.rb CHANGED
@@ -5,7 +5,7 @@ require 'version_sorter'
5
5
  require 'digest/md5'
6
6
 
7
7
  module Jim
8
- VERSION = '0.2.3'
8
+ VERSION = '0.3.0.pre'
9
9
 
10
10
  class Error < RuntimeError; end
11
11
  class InstallError < Error; end
data/lib/jim/bundler.rb CHANGED
@@ -3,99 +3,170 @@ module Jim
3
3
  # versions and then can bundle, compress, or copy those files into specific dirs
4
4
  # or files.
5
5
  #
6
- # A Jimfile has a really simple format:
7
- #
8
- # // comments look like JS comments
9
- # // you can set options by adding comments that look like JSON pairs
10
- # // bundle_path: /path/to/bundle.js
11
- #
12
- # // A requirement is just a name and an optional version
13
- # // requirements are resolved and bundled in order of specification
14
- # jquery 1.4.2
15
- # jquery.color
16
- # sammy 0.5.0
6
+ # A Jimfile has a really simple format that's encoded as JSON. Options are
7
+ # specified as key value pairs and 'bundles' are specified under the "bundles"
8
+ # attribute. Each item in a bundle can be a simple string, or an array of
9
+ # [name, version].
17
10
  #
11
+ # {
12
+ # "bundle_dir": "test/tmp/",
13
+ # "vendor_dir": "test/tmp/public/javascripts/vendor",
14
+ # "bundles": {
15
+ # "default": [
16
+ # ["jquery", "1.4.1"],
17
+ # "myproject",
18
+ # "localfile"
19
+ # ],
20
+ # "base": [
21
+ # "jquery"
22
+ # ]
23
+ # }
24
+ # }
18
25
  #
26
+ # Pre jim version 0.3 had a different simpler but proprietary and (possibly confusing)
27
+ # Jimfile format. Bundler can still read that format and can actually convert
28
+ # it into the new JSON format for you. Future versions may remove this support.
19
29
  class Bundler
20
30
  class MissingFile < Jim::Error; end
31
+ class InvalidBundle < Jim::Error; end
21
32
 
22
- attr_accessor :jimfile, :index, :requirements, :paths, :options
33
+ attr_accessor :jimfile, :index, :bundles, :paths, :options
34
+ attr_reader :jimfile, :bundle_dir
23
35
 
24
36
  # create a new bundler instance passing in the Jimfile as a `Pathname` or a
25
37
  # string. `index` is a Jim::Index
26
38
  def initialize(jimfile, index = nil, extra_options = {})
27
- self.jimfile = jimfile.is_a?(Pathname) ? jimfile.read : jimfile
28
39
  self.index = index || Jim::Index.new
29
- self.options = {}
30
- self.requirements = []
31
- parse_jimfile
40
+ self.options = {
41
+ :compressed_suffix => '.min'
42
+ }
43
+ self.bundles = {}
44
+ self.jimfile = jimfile
32
45
  self.options = options.merge(extra_options)
33
- self.paths = []
46
+ self.paths = {}
34
47
  if options[:vendor_dir]
35
48
  logger.debug "adding vendor dir to index #{options[:vendor_dir]}"
36
49
  self.index.add(options[:vendor_dir])
37
50
  end
38
51
  end
39
52
 
40
- # resove the requirements specified into Jimfile or raise a MissingFile error
53
+ # Set the Jimfile and parse it. If `file` is a `Pathname` read it as a string.
54
+ # If it's a string assume that its in the Jimfile format.
55
+ def jimfile=(file)
56
+ @jimfile = file.is_a?(Pathname) ? file.read : file
57
+ # look for old jimfile
58
+ if @jimfile =~ /^\/\//
59
+ logger.warn "You're Jimfile is in a deprecated format. Run `jim update_jimfile` to convert it."
60
+ parse_old_jimfile
61
+ else
62
+ parse_jimfile
63
+ end
64
+ @jimfile
65
+ end
66
+
67
+ # Set the `bundle_dir` where bundles will be written. If `bundle_dir` is set
68
+ # to nil, all bundles will be written to STDOUT
69
+ def bundle_dir=(new_dir)
70
+ if new_dir
71
+ new_dir = Pathname.new(new_dir)
72
+ new_dir.mkpath
73
+ end
74
+ @bundle_dir = new_dir
75
+ end
76
+
77
+ # Output the parse Jimfile requirements and options as a Jimfile-ready
78
+ # JSON-encoded string
79
+ def jimfile_to_json
80
+ h = {
81
+ "bundle_dir" => bundle_dir
82
+ }.merge(options)
83
+ h['bundles'] = {}
84
+ self.bundles.each do |bundle_name, requirements|
85
+ h['bundles']['bundle_name'] = []
86
+ requirements.each do |name, version|
87
+ h['bundles']['bundle_name'] << if version.nil? || version.strip == ''
88
+ name
89
+ else
90
+ [name, version]
91
+ end
92
+ end
93
+ end
94
+ Yajl::Encoder.encode(h, :pretty => true)
95
+ end
96
+
97
+ # Resolve the requirements specified in the Jimfile for each bundle to `paths`
98
+ # Raises MissingFile error
41
99
  def resolve!
42
- self.requirements.each do |search|
43
- name, version = search.strip.split(/\s+/)
44
- path = self.index.find(name, version)
45
- if !path
46
- raise(MissingFile,
47
- "Could not find #{name} #{version} in any of these paths #{index.directories.join(':')}")
100
+ self.bundles.each do |bundle_name, requirements|
101
+ self.paths[bundle_name] = []
102
+ requirements.each do |name, version|
103
+ path = self.index.find(name, version)
104
+ if !path
105
+ raise(MissingFile,
106
+ "Could not find #{name} #{version} in any of these paths #{index.directories.join(':')}")
107
+ end
108
+ self.paths[bundle_name] << [path, name, version]
48
109
  end
49
- self.paths << [path, name, version]
50
110
  end
51
111
  paths
52
112
  end
53
113
 
54
- # concatenate all the requirements into a single file and write to `to` or to the
55
- # path specified in the :bundled_path option
56
- def bundle!(to = nil)
114
+ # Concatenate all of the bundles to the dir set in `bundle_dir`
115
+ # or a specific bundle specified by bundle name. Setting `compress` to
116
+ # true will run the output of each bundle to the Google Closure Compiler.
117
+ # You can also use the YUI compressor by setting the option :compressor to 'yui'
118
+ # Raises an error if there is no bundled dir or specific bundle set
119
+ def bundle!(bundle_name = false, compress = false)
57
120
  resolve! if paths.empty?
58
- to = options[:bundled_path] if to.nil? && options[:bundled_path]
59
- io_for_path(to) do |io|
60
- logger.info "Bundling to #{to}" if to
61
- paths.each do |path, name, version|
62
- io << path.read << "\n"
121
+ if bundle_name
122
+ files = self.paths[bundle_name]
123
+ if bundle_dir
124
+ path = path_for_bundle(bundle_name, compress)
125
+ concatenate(files, path, compress)
126
+ [path]
127
+ else
128
+ concatenate(files, "", compress)
63
129
  end
130
+ elsif bundle_dir
131
+ self.paths.collect do |bundle_name, files|
132
+ path = path_for_bundle(bundle_name, compress)
133
+ concatenate(files, path, compress)
134
+ path
135
+ end
136
+ else
137
+ raise(InvalidBundle,
138
+ "Must set either a :bundle_dir to write files to or a specific bundle to write to STDOUT")
64
139
  end
65
140
  end
66
141
 
67
- # concatenate all the requirements into a single file then run through a JS
68
- # then write to `to` or to the path specified in the :bundled_path option.
69
- # You can also use the YUI compressor by setting the option :compressor to 'yui'
70
- def compress!(to = nil)
71
- to = options[:compressed_path] if to.nil? && options[:compressed_path]
72
- io_for_path(to) do |io|
73
- logger.info "Compressing to #{to}"
74
- io << compress_js(bundle!(false))
75
- end
142
+ # Alias to running `bundle!` with `compress` = `true`
143
+ def compress!(bundle_name = false)
144
+ bundle!(bundle_name, true)
76
145
  end
77
146
 
78
- # copy each of the requirements into the dir specified with `dir` or the path
79
- # specified with the :vendor_dir option
147
+ # Copy each of the requirements into the dir specified with `dir` or the path
148
+ # specified with the :vendor_dir option. Returns the dir it was vendored to.
80
149
  def vendor!(dir = nil, force = false)
81
150
  resolve! if paths.empty?
82
151
  dir ||= options[:vendor_dir]
83
152
  dir ||= 'vendor' # default
84
- logger.info "Vendoring to #{dir}"
85
- paths.each do |path, name, version|
153
+ logger.debug "Vendoring #{paths.length} files to #{dir}"
154
+ paths.collect {|n, p| p }.flatten.each do |path, name, version|
86
155
  if index.in_jimhome?(path)
87
156
  Jim::Installer.new(path, dir, :shallow => true, :force => force).install
88
157
  end
89
158
  end
159
+ dir
160
+ end
161
+
162
+ # Returns an array of `Pathname`s where each of the bundles will be written
163
+ def bundle_paths
164
+ self.bundles.collect {|name, reqs| path_for_bundle(name) }
90
165
  end
91
166
 
92
- # Run the uncompressed test through a JS compressor (closure-compiler) by
167
+ # Run the uncompressed js through a JS compressor (closure-compiler) by
93
168
  # default. Setting options[:compressor] == 'yui' will force the YUI JS Compressor
94
169
  def compress_js(uncompressed)
95
- # if uncompressed.is_a?(File) && uncompressed.closed?
96
- # puts "uncompressed is a file"
97
- # uncompressed = File.read(uncompressed.path)
98
- # end
99
170
  if options[:compressor] == 'yui'
100
171
  begin
101
172
  require "yui/compressor"
@@ -111,40 +182,64 @@ module Jim
111
182
  end
112
183
  compressor = ::Closure::Compiler.new
113
184
  end
114
- compressor.compress(uncompressed)
185
+ begin
186
+ compressor.compress(uncompressed)
187
+ rescue Exception => e
188
+ logger.error e.message
189
+ end
115
190
  end
116
191
 
117
192
  private
118
- def io_for_path(to, &block)
119
- case to
120
- when IO
121
- yield to
122
- to.close
123
- to
124
- when Pathname
125
- to.dirname.mkpath
126
- io = to.open('w')
127
- yield io
128
- io.close
129
- io
130
- when String
131
- to = Pathname.new(to)
132
- io_for_path(to, &block)
133
- else
134
- io = ""
135
- yield io
136
- io
193
+ def concatenate(paths, io, compress)
194
+ if io.is_a?(Pathname)
195
+ io = io.open('w')
196
+ logger.debug "#{compress ? 'Compressing' : 'Bundling'} to #{io}"
137
197
  end
198
+ final_io, io = io, "" if compress
199
+ paths.each do |path, name, version|
200
+ io << path.read << "\n"
201
+ end
202
+ if compress
203
+ final_io << compress_js(io)
204
+ io = final_io
205
+ end
206
+ io.close if io.respond_to?(:close)
207
+ io
208
+ end
209
+
210
+ def path_for_bundle(bundle_name, compressed = false)
211
+ bundle_dir + "#{bundle_name}#{compressed ? options[:compressed_suffix] : ''}.js"
138
212
  end
139
213
 
140
214
  def parse_jimfile
215
+ json = Yajl::Parser.parse(jimfile)
216
+ json.each do |k, v|
217
+ set_option(k, v)
218
+ end
219
+ end
220
+
221
+ def parse_old_jimfile
222
+ bundle = []
141
223
  jimfile.each_line do |line|
142
224
  if /^\/\/\s?([^\:]+)\:\s(.*)$/.match line
143
- self.options[$1.to_sym] = $2.strip
225
+ k, v = $1, $2.strip
226
+ if k == 'bundled_path'
227
+ k, v = 'bundle_dir', File.dirname(v)
228
+ end
229
+ set_option(k, v)
144
230
  elsif line !~ /^\// && line.strip != ''
145
- self.requirements << line
231
+ bundle << line.split(/\s+/, 2).compact.collect {|s| s.strip }.reject {|s| s == '' }
146
232
  end
147
233
  end
234
+ self.bundles['default'] = bundle
235
+ end
236
+
237
+ def set_option(k, v)
238
+ if respond_to?("#{k}=")
239
+ self.send("#{k}=", v)
240
+ else
241
+ self.options[k.to_sym] = v
242
+ end
148
243
  end
149
244
 
150
245
  def logger
data/lib/jim/cli.rb CHANGED
@@ -1,212 +1,250 @@
1
- require 'optparse'
2
- require 'readline'
1
+ require 'thor'
3
2
 
4
3
  module Jim
4
+ # CLI handles the command line interface for the `jim` binary. It is a `Thor`
5
+ # application.
6
+ class CLI < ::Thor
7
+ include Thor::Actions
5
8
 
6
- # CLI handles the command line interface for the `jim` binary.
7
- # The layout is farily simple. Options are parsed using optparse.rb and
8
- # the different public methods represent 1-1 the commands provided by the bin.
9
- class CLI
9
+ attr_accessor :jimfile, :jimhome, :debug, :force
10
10
 
11
- attr_accessor :jimfile, :jimhome, :force, :stdout
11
+ source_root File.dirname(__FILE__) + '/templates'
12
+
13
+ class_option "jimhome",
14
+ :type => :string,
15
+ :banner => "set the install path/JIMHOME dir (default ~/.jim)"
16
+
17
+ class_option "jimfile",
18
+ :type => :string,
19
+ :aliases => '-j',
20
+ :banner => "load specific Jimfile at path (default ./Jimfile)"
21
+
22
+ class_option "force",
23
+ :default => false,
24
+ :aliases => '-f',
25
+ :banner => "force file creation/overwrite"
26
+
27
+ class_option "debug",
28
+ :default => false,
29
+ :aliases => '-d',
30
+ :banner => "set log level to debug"
31
+
32
+ class_option "version",
33
+ :type => :boolean,
34
+ :aliases => '-v',
35
+ :banner => "print version and exit"
12
36
 
13
37
  # create a new instance with the args passed from the command line i.e. ARGV
14
- def initialize(args)
15
- @output = ""
38
+ def initialize(*)
39
+ super
40
+ if options[:version]
41
+ say "jim #{Jim::VERSION}", :red
42
+ exit
43
+ end
16
44
  # set the default jimhome
17
- self.jimhome = Pathname.new(ENV['JIMHOME'] || '~/.jim').expand_path
45
+ self.jimhome = Pathname.new(options[:jimhome] || ENV['JIMHOME'] || '~/.jim').expand_path
18
46
  # parse the options
19
- self.jimfile = Pathname.new('Jimfile')
20
- @args = parse_options(args)
21
- ## try to run based on args
22
- end
23
-
24
- # method called by the bin directly after initialization.
25
- def run(reraise = false)
26
- command = @args.shift
27
- if command && respond_to?(command)
28
- self.send(command, *@args)
29
- elsif command.nil? || command.strip == ''
30
- cheat
31
- else
32
- @output << "No action found for #{command}. Run -h for help."
33
- end
34
- @output
35
- rescue ArgumentError => e
36
- @output << "#{e.message} for #{command}"
37
- raise e if reraise
38
- rescue Jim::FileExists => e
39
- @output << "#{e.message} already exists, bailing. Use --force if you're sure"
40
- raise e if reraise
41
- rescue => e
42
- @output << e.message + " (#{e.class})"
43
- raise e if reraise
44
- end
45
-
46
- # list the possible commands to the logger
47
- def commands
48
- logger.info "Usage: jim [options] [command] [args]\n"
49
- logger.info "Commands:"
50
- logger.info template('commands')
51
- end
52
-
53
- # list the possible commands without detailed descriptions
54
- def cheat
55
- logger.info "Usage: jim [options] [command] [args]\n"
56
- logger.info "Commands:"
57
- logger.info [*template('commands')].grep(/^\w/).join
58
- logger.info "run commands for details"
59
- end
60
- alias :help :cheat
61
-
62
- # initialize the current dir with a new Jimfile
47
+ self.jimfile = Pathname.new(options[:jimfile] || 'Jimfile').expand_path
48
+ self.force = options[:force]
49
+ self.debug = options[:debug]
50
+ logger.level = Logger::DEBUG if debug
51
+ end
52
+
53
+ desc 'init [APPDIR]',
54
+ 'Create an example Jimfile at path or the current directory if path is omitted'
63
55
  def init(dir = nil)
64
56
  dir = Pathname.new(dir || '')
65
57
  jimfile_path = dir + 'Jimfile'
66
- if jimfile_path.readable? && !force
67
- raise Jim::FileExists.new(jimfile_path)
68
- else
69
- File.open(jimfile_path, 'w') do |f|
70
- f << template('jimfile')
71
- end
72
- logger.info "wrote Jimfile to #{jimfile_path}"
73
- end
58
+ template('jimfile', jimfile_path)
74
59
  end
75
60
 
76
- # install the file/project `url` into `jimhome`
61
+ desc 'install <URL> [NAME] [VERSION]',
62
+ "Install the file(s) at url into the JIMHOME directory."
63
+
64
+ long_desc <<-EOT
65
+ Install the file(s) at url into the JIMHOME directory. URL can be any path
66
+ or url that Downlow understands. This means:
67
+
68
+ jim install http://code.jquery.com/jquery-1.4.1.js\n
69
+ jim install ../sammy/\n
70
+ jim install gh://quirkey/sammy\n
71
+ jim install git://github.com/jquery/jquery.git\n
72
+ EOT
77
73
  def install(url, name = false, version = false)
78
74
  Jim::Installer.new(url, jimhome, :force => force, :name => name, :version => version).install
79
75
  end
80
76
 
81
- # bundle the files specified in Jimfile into `to`
82
- def bundle(to = nil)
83
- to = STDOUT if stdout
84
- io = bundler.bundle!(to)
85
- logger.info "Wrote #{File.size(io.path) / 1024}kb" if io.respond_to? :path
77
+ desc 'bundle [BUNDLE_NAME]',
78
+ "Bundle the files specified in Jimfile"
79
+ long_desc <<-EOT
80
+ Concatenate all the bundles listed in a Jimfile and save them to the bundle dir
81
+ specified in the options or in the Jimfile.
82
+
83
+ If [BUNDLE_NAME] is specified, only bundles that specific bundle.
84
+
85
+ If no Jimfile is set in the options, assumes ./Jimfile.
86
+ EOT
87
+ method_option "bundle_dir",
88
+ :type => :string,
89
+ :banner => "override the bundle_dir set in the Jimfile"
90
+ method_option "stdout",
91
+ :default => false,
92
+ :aliases => '-o',
93
+ :type => :boolean,
94
+ :banner => "write the bundle to STDOUT"
95
+ def bundle(bundle_name = nil)
96
+ make_bundle(bundle_name, false)
86
97
  end
87
98
 
88
- # compress the files specified in Jimfile into `to`
89
- def compress(to = nil)
90
- to = STDOUT if stdout
91
- io = bundler.compress!(to)
92
- logger.info "Wrote #{File.size(io.path) / 1024}kb" if io.respond_to? :path
99
+ desc "compress [BUNDLE_NAME]",
100
+ "Bundle all the files listed in a Jimfile, run through the google closure " +
101
+ "compiler and save them to [COMPRESSED_PATH]."
102
+ long_desc <<-EOT
103
+ Concatenate all the bundles listed in a Jimfile, run them through the google
104
+ closure compiler and save them to the bundle dirspecified in the options or
105
+ in the Jimfile.
106
+
107
+ If a [BUNDLE_NAME] is specified, only bundle and compress that bundle.
108
+
109
+ If no Jimfile is set in the options, assumes ./Jimfile.
110
+ EOT
111
+ method_option "bundle_dir",
112
+ :type => :string,
113
+ :banner => "override the bundle_dir set in the Jimfile"
114
+ method_option "stdout",
115
+ :default => false,
116
+ :aliases => '-o',
117
+ :type => :boolean,
118
+ :banner => "write the bundle to STDOUT"
119
+ def compress(bundle_name = nil)
120
+ make_bundle(bundle_name, true)
93
121
  end
94
122
 
95
- # copy/vendor all the files specified in Jimfile to `dir`
123
+ desc "vendor [VENDOR_DIR]",
124
+ "Copy all the files listed in Jimfile to the vendor_dir"
96
125
  def vendor(dir = nil)
97
- bundler.vendor!(dir, force)
126
+ dir = bundler.vendor!(dir, force)
127
+ say("Vendored files to #{dir}", :green)
128
+ rescue Jim::Error => e
129
+ say e.message, :red
98
130
  end
99
131
 
100
- # list the only the _installed_ projects and versions.
101
- # Match names against `search` if supplied.
132
+ desc "list [SEARCH]",
133
+ "List all the installed packages and their versions, optionally limiting by [SEARCH]"
102
134
  def list(search = nil)
103
- logger.info "Getting list of installed files in\n#{installed_index.directories.join(':')}"
104
- logger.info "Searching for '#{search}'" if search
135
+ say "Getting list of installed files in"
136
+ say("#{installed_index.directories.join(':')}", :yellow)
137
+ say("Searching for '#{search}'", :yellow) if search
105
138
  list = installed_index.list(search)
106
- logger.info "Installed:"
139
+ say "Installed:"
107
140
  print_version_list(list)
108
141
  end
109
- alias :installed :list
142
+ map "installed" => "list"
110
143
 
111
- # list all available projects and versions including those in the local path, or
112
- # paths specified in a Jimfile.
113
- # Match names against `search` if supplied.
144
+ desc "available [SEARCH]",
145
+ "List all available projects and versions " +
146
+ "including those in the local path, or paths specified in a Jimfile"
114
147
  def available(search = nil)
115
- logger.info "Getting list of all available files in\n#{index.directories.join("\n")}"
116
- logger.info "Searching for '#{search}'" if search
148
+ say "Getting list of all available files in\n#{index.directories.join("\n")}"
149
+ say "Searching for '#{search}'" if search
117
150
  list = index.list(search)
118
- logger.info "Available:"
151
+ say "Available:"
119
152
  print_version_list(list)
120
153
  end
121
154
 
122
- # Iterates over matching files and prompts for removal
155
+ desc "remove <NAME> [VERSION]",
156
+ "Iterate through the install files and prompt for the removal of those " +
157
+ "matching the supplied NAME and VERSION"
123
158
  def remove(name, version = nil)
124
- logger.info "Looking for files matching #{name} #{version}"
159
+ say "Looking for files matching #{name} #{version}"
125
160
  files = installed_index.find_all(name, version)
126
161
  if files.length > 0
127
- logger.info "Found #{files.length} matching files"
162
+ say "Found #{files.length} matching files"
128
163
  removed = 0
129
164
  files.each do |filename|
130
- response = Readline.readline("Remove #{filename}? (y/n)\n")
131
- if response.strip =~ /y/i
132
- logger.info "Removing #{filename}"
165
+ do_remove = yes?("Remove #{filename}?", :red)
166
+ if do_remove
167
+ say "Removing #{filename}"
133
168
  filename.delete
134
169
  removed += 1
135
170
  else
136
- logger.info "Skipping #{filename}"
171
+ say "Skipping #{filename}", :yellow
137
172
  end
138
173
  end
139
- logger.info "Removed #{removed} files."
174
+ say "Removed #{removed} files."
140
175
  else
141
- logger.info "No installed files matched."
176
+ say "No installed files matched."
142
177
  end
143
178
  end
144
- alias :uninstall :remove
179
+ map "uninstall" => "remove"
145
180
 
146
- # list the files and their resolved paths specified in the Jimfile
181
+ desc "resolve",
182
+ "Resolve all the paths listed in a Jimfile and print them to STDOUT. " +
183
+ "If no Jimfile is set in the options, assumes ./Jimfile."
147
184
  def resolve
148
185
  resolved = bundler.resolve!
149
- logger.info "Files:"
150
- resolved.each do |r|
151
- logger.info r.join(" | ")
186
+ say "Files:"
187
+ resolved.each do |bundle_name, requirements|
188
+ say bundle_name, :green
189
+ say "-----------------------", :green
190
+ requirements.each do |path, name, version|
191
+ say [name, version, path].join(" | ") + "\n"
192
+ end
152
193
  end
153
194
  resolved
195
+ rescue Jim::Error => e
196
+ say e.message, :red
154
197
  end
155
198
 
156
- # vendor to dir, then bundle and compress the Jimfile contents
199
+ desc "pack [DIR]",
200
+ "Runs in order, vendor, bundle, compress. This command " +
201
+ "simplifies the common workflow of vendoring and re-bundling " +
202
+ "before committing or deploying changes to a project"
157
203
  def pack(dir = nil)
158
- logger.info "packing the Jimfile for this project"
159
- vendor(dir)
160
- bundle
161
- compress
204
+ say "Packing the Jimfile for this project"
205
+ invoke :vendor, [dir]
206
+ invoke :bundle
207
+ invoke :compress
162
208
  end
163
209
 
164
- private
165
- def parse_options(runtime_args)
166
- OptionParser.new("", 24, ' ') do |opts|
167
- opts.banner = "Usage: jim [options] [command] [args]"
168
-
169
- opts.separator ""
170
- opts.separator "jim options:"
171
-
172
- opts.on("--jimhome path/to/home", "set the install path/JIMHOME dir (default ~/.jim)") {|h|
173
- self.jimhome = Pathname.new(h)
174
- }
175
-
176
- opts.on("-j", "--jimfile path/to/jimfile", "load specific Jimfile at path (default ./Jimfile)") { |j|
177
- self.jimfile = Pathname.new(j)
178
- }
179
-
180
- opts.on("-f", "--force", "force file creation/overwrite") {|f|
181
- self.force = true
182
- }
183
-
184
- opts.on("-d", "--debug", "set log level to debug") {|d|
185
- logger.level = Logger::DEBUG
186
- }
187
-
188
- opts.on("-o", "--stdout", "write output of commands (like bundle and compress to STDOUT)") {|o|
189
- logger.level = Logger::ERROR
190
- self.stdout = true
191
- }
192
-
193
- opts.on("-v", "--version", "print version") {|d|
194
- puts "jim #{Jim::VERSION}"
195
- exit
196
- }
210
+ desc "watch [DIR]",
211
+ "Watches your Jimfile and JS files and triggers `bundle` if something " +
212
+ "changes. Handy for development."
213
+ def watch(dir = nil)
214
+ require 'fssm'
215
+ run_update = lambda {|type, path|
216
+ unless bundler.bundle_paths.any? {|p| path.include?(p) }
217
+ say("--> #{path} #{type}")
218
+ system "jim bundle"
219
+ end
220
+ }
221
+ say "Now watching JS files..."
222
+ run_update["started", 'Jimfile']
223
+ FSSM.monitor(Dir.pwd, [File.join('**','*.js'), 'Jimfile']) do
224
+ update do |base, relative|
225
+ run_update["changed", relative]
226
+ end
197
227
 
228
+ create do |base, relative|
229
+ run_update["created", relative]
230
+ end
198
231
 
199
- opts.on_tail("-h", "--help", "Show this message. Run jim commands for list of commands.") do
200
- puts opts.help
201
- exit
232
+ delete do |base, relative|
233
+ run_update["deleted", relative]
202
234
  end
235
+ end
236
+ end
203
237
 
204
- end.parse! runtime_args
205
- rescue OptionParser::MissingArgument => e
206
- logger.warn "#{e}, run -h for options"
207
- exit
238
+ desc "update_jimfile [APP_DIR]",
239
+ "Converts a Jimfile from the old pre 0.3 format to the JSON format."
240
+ def update_jimfile(dir = nil)
241
+ dir = Pathname.new(dir || '')
242
+ bundler
243
+ copy_file(dir + 'Jimfile', dir + 'Jimfile.old')
244
+ create_file(dir + 'Jimfile', bundler.jimfile_to_json)
208
245
  end
209
246
 
247
+ private
210
248
  def index
211
249
  @index ||= Jim::Index.new(install_dir, Dir.pwd)
212
250
  end
@@ -223,18 +261,29 @@ module Jim
223
261
  jimhome + 'lib'
224
262
  end
225
263
 
226
- def template(path)
227
- (Pathname.new(__FILE__).dirname + 'templates' + path).read
228
- end
229
-
230
264
  def logger
231
265
  Jim.logger
232
266
  end
233
267
 
234
268
  def print_version_list(list)
235
269
  list.each do |file, versions|
236
- logger.info "#{file} (#{VersionSorter.rsort(versions.collect {|v| v[0] }).join(', ')})"
270
+ say "#{file} (#{VersionSorter.rsort(versions.collect {|v| v[0] }).join(', ')}\n"
271
+ end
272
+ end
273
+
274
+ def make_bundle(bundle_name, compress = false)
275
+ bundler.bundle_dir = options[:bundle_dir] if options[:bundle_dir]
276
+ bundler.bundle_dir = nil if options[:stdout]
277
+ result = bundler.bundle!(bundle_name, compress)
278
+ if options[:stdout]
279
+ puts result
280
+ else
281
+ result.each do |path|
282
+ say("Wrote #{path} #{File.size(path.to_s) / 1024}kb", :green)
283
+ end
237
284
  end
285
+ rescue Jim::Error => e
286
+ say e.message, :red
238
287
  end
239
288
 
240
289
  end