jim 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+ tmp
21
+
22
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Aaron Quint
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,87 @@
1
+ = jim
2
+
3
+ jim is your friendly javascript library manager.
4
+ He downloads, stores, bundles, vendors and compresses.
5
+
6
+ == What is a jim?
7
+
8
+ I'm frustrated with a lot of copy and pasting JS files from one directory to another and downloading new versions to who knows where and only really being able to use sprockets in Rails and lots of other small annoying things about the existing JS package/asset managers.
9
+
10
+ Jim uses a lot of stolen ideas from a lot of great projects. Namely:
11
+
12
+ * version management and flexible project fetching from rip
13
+ * asset compression from jammit and sprockets
14
+ * Gemfile and bundle from bundler
15
+
16
+ The goals are simple:
17
+
18
+ * Install a file, archive, git repo, etc from a local path or a URL into a common directory stored with a specific name and version number. Because should be able to install anything from anywhere, theres no need for a central package host.
19
+ * Specify the local and installed files required for a project by name and version number in a single place. Dependencies for each project are not managed by the system, the onus is on you to specify them in the order you want them. _The project does not have to be in Ruby or even have a backend system._
20
+ * Run a command to bundle all the required files into a single file.
21
+ * Run a command to bundle the files _and_ run them through a JavaScript compressor.
22
+
23
+ So far I've accomplished the goals, but this is all very very very beta and the API is sure to change and thing straight-up might not work.
24
+
25
+ == Usage
26
+
27
+ From anywhere, install a project:
28
+
29
+ // From a URL
30
+ $ jim install http://code.jquery.com/jquery-1.4.2.js
31
+ // From a zip (with name and version)
32
+ $ jim install http://github.com/jquery/jquery-metadata/zipball/master jquery-metadata 2.0
33
+
34
+ In your project run:
35
+
36
+ $ jim init
37
+
38
+ Which create an empty "Jimfile". Open it up and add your requirements:
39
+
40
+ // bundled_path: public/javascripts/bundled.js
41
+ // compressed_path: public/javascripts/compressed.js
42
+ // vendor_dir: public/javascripts/vendor
43
+
44
+ jquery 1.4.2
45
+ jquery-metadata 2.0
46
+ // a local file (comments are fine)
47
+ app.js
48
+
49
+ If its a rack project, mount the Jim::Rack middleware which gives you live updates of your bundled and compressed js files:
50
+
51
+ use Jim::Rack, :bundled_uri => '/javascripts/bundled-live.js',
52
+ :compressed_uri => '/javascripts/compressed-live.js'
53
+
54
+
55
+ Otherwise and also before deploys, etc, use the command line tool from your project's dir:
56
+
57
+ $ jim bundle
58
+ // or
59
+ $ jim compress
60
+
61
+ Run `jim commands` for a full list of commands.
62
+
63
+ == You're probably wondering
64
+
65
+ ==== Why not implement it in JavaScript itself?
66
+
67
+ CommonJS has certainly come a long way in a year, but in general the file system support and variety of libraries just isnt completely there yet for this type of project (IMHO). Also, I love Ruby and writing this was actually fun and pretty fast.
68
+
69
+ With that said, I would gladly welcome anyone cloning the API in CommonJS (I'm just being lazy about it)
70
+
71
+ ==== Does it work with __ library or __ development platform?
72
+
73
+ Probably?? Its all very new at this point so please test it out and let me know.
74
+
75
+ == Note on Patches/Pull Requests
76
+
77
+ * Fork the project.
78
+ * Make your feature addition or bug fix.
79
+ * Add tests for it. This is important so I don't break it in a
80
+ future version unintentionally.
81
+ * Commit, do not mess with rakefile, version, or history.
82
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
83
+ * Send me a pull request. Bonus points for topic branches.
84
+
85
+ == Copyright
86
+
87
+ Copyright (c) 2010 Aaron Quint. See LICENSE for details.
@@ -0,0 +1,59 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
5
+
6
+ require 'jim'
7
+
8
+ begin
9
+ require 'jeweler'
10
+ Jeweler::Tasks.new do |gem|
11
+ gem.name = "jim"
12
+ gem.version = Jim::VERSION
13
+ gem.summary = %Q{jim is your friendly javascript library manager}
14
+ gem.description = %Q{jim is your friendly javascript library manager. He downloads, stores, bundles, vendors and compresses.}
15
+ gem.email = "aaron@quirkey.com"
16
+ gem.homepage = "http://github.com/quirkey/jim"
17
+ gem.authors = ["Aaron Quint"]
18
+ gem.add_dependency "downlow", ">= 0.1.1"
19
+ gem.add_development_dependency "shoulda", ">= 0"
20
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
21
+ end
22
+ Jeweler::GemcutterTasks.new
23
+ rescue LoadError
24
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
25
+ end
26
+
27
+ require 'rake/testtask'
28
+ Rake::TestTask.new(:test) do |test|
29
+ test.libs << 'lib' << 'test'
30
+ test.pattern = 'test/**/test_*.rb'
31
+ test.verbose = true
32
+ end
33
+
34
+ begin
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ end
41
+ rescue LoadError
42
+ task :rcov do
43
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
44
+ end
45
+ end
46
+
47
+ task :test => :check_dependencies
48
+
49
+ task :default => :test
50
+
51
+ require 'rake/rdoctask'
52
+ Rake::RDocTask.new do |rdoc|
53
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
54
+
55
+ rdoc.rdoc_dir = 'rdoc'
56
+ rdoc.title = "jim #{version}"
57
+ rdoc.rdoc_files.include('README*')
58
+ rdoc.rdoc_files.include('lib/**/*.rb')
59
+ end
data/bin/jim ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jim'
4
+
5
+ cli = Jim::CLI.new(ARGV)
6
+ puts cli.run
@@ -0,0 +1,83 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{jim}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Aaron Quint"]
12
+ s.date = %q{2010-02-19}
13
+ s.default_executable = %q{jim}
14
+ s.description = %q{jim is your friendly javascript library manager. He downloads, stores, bundles, vendors and compresses.}
15
+ s.email = %q{aaron@quirkey.com}
16
+ s.executables = ["jim"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "bin/jim",
28
+ "jim.gemspec",
29
+ "lib/jim.rb",
30
+ "lib/jim/bundler.rb",
31
+ "lib/jim/cli.rb",
32
+ "lib/jim/index.rb",
33
+ "lib/jim/installer.rb",
34
+ "lib/jim/rack.rb",
35
+ "lib/jim/templates/commands",
36
+ "lib/jim/templates/jimfile",
37
+ "lib/jim/version_parser.rb",
38
+ "test/fixtures/infoincomments.js",
39
+ "test/fixtures/jimfile",
40
+ "test/fixtures/jquery-1.4.1.js",
41
+ "test/fixtures/jquery.color.js",
42
+ "test/fixtures/jquery.metadata-2.0.zip",
43
+ "test/fixtures/mustache.js/mustache.js",
44
+ "test/fixtures/mustache.js/package.json",
45
+ "test/fixtures/noversion.js",
46
+ "test/helper.rb",
47
+ "test/test_jim_bundler.rb",
48
+ "test/test_jim_cli.rb",
49
+ "test/test_jim_index.rb",
50
+ "test/test_jim_installer.rb",
51
+ "test/test_jim_version_parser.rb"
52
+ ]
53
+ s.homepage = %q{http://github.com/quirkey/jim}
54
+ s.rdoc_options = ["--charset=UTF-8"]
55
+ s.require_paths = ["lib"]
56
+ s.rubygems_version = %q{1.3.5}
57
+ s.summary = %q{jim is your friendly javascript library manager}
58
+ s.test_files = [
59
+ "test/helper.rb",
60
+ "test/test_jim_bundler.rb",
61
+ "test/test_jim_cli.rb",
62
+ "test/test_jim_index.rb",
63
+ "test/test_jim_installer.rb",
64
+ "test/test_jim_version_parser.rb"
65
+ ]
66
+
67
+ if s.respond_to? :specification_version then
68
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
69
+ s.specification_version = 3
70
+
71
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
72
+ s.add_runtime_dependency(%q<downlow>, [">= 0.1.1"])
73
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
74
+ else
75
+ s.add_dependency(%q<downlow>, [">= 0.1.1"])
76
+ s.add_dependency(%q<shoulda>, [">= 0"])
77
+ end
78
+ else
79
+ s.add_dependency(%q<downlow>, [">= 0.1.1"])
80
+ s.add_dependency(%q<shoulda>, [">= 0"])
81
+ end
82
+ end
83
+
@@ -0,0 +1,31 @@
1
+ require 'downlow'
2
+ require 'logger'
3
+ require 'yajl'
4
+
5
+ module Jim
6
+ VERSION = '0.1.0'
7
+
8
+ class Error < RuntimeError; end
9
+ class FileExists < Error; end
10
+
11
+ def self.logger=(logger)
12
+ @logger = logger
13
+ end
14
+
15
+ def self.logger
16
+ @logger ||= LOGGER if defined?(LOGGER)
17
+ if !@logger
18
+ @logger = Logger.new(STDOUT)
19
+ @logger.level = Logger::INFO
20
+ @logger.formatter = Proc.new {|s, t, n, msg| "#{msg}\n"}
21
+ @logger
22
+ end
23
+ @logger
24
+ end
25
+
26
+ autoload :Installer, 'jim/installer'
27
+ autoload :Index, 'jim/index'
28
+ autoload :Bundler, 'jim/bundler'
29
+ autoload :VersionParser, 'jim/version_parser'
30
+ autoload :CLI, 'jim/cli'
31
+ end
@@ -0,0 +1,102 @@
1
+ module Jim
2
+ class Bundler
3
+ class MissingFile < Jim::Error; end
4
+
5
+ attr_accessor :jimfile, :index, :requirements, :paths, :options
6
+
7
+ def initialize(jimfile, index, options = {})
8
+ self.jimfile = jimfile.is_a?(Pathname) ? jimfile.read : jimfile
9
+ self.index = index || Jim::Index.new
10
+ self.options = {}
11
+ self.requirements = []
12
+ parse_jimfile
13
+ self.options.merge(options)
14
+ self.add(options[:vendor_dir]) if options[:vendor_dir]
15
+ self.paths = []
16
+ end
17
+
18
+ def resolve!
19
+ self.requirements.each do |search|
20
+ name, version = search.strip.split(/\s+/)
21
+ path = self.index.find(name, version)
22
+ if !path
23
+ raise(MissingFile,
24
+ "Could not find #{name} #{version} in any of these paths #{index.directories.join(':')}")
25
+ end
26
+ self.paths << path
27
+ end
28
+ paths
29
+ end
30
+
31
+ def bundle!(to = nil)
32
+ resolve! if paths.empty?
33
+ to = options[:bundled_path] if to.nil? && options[:bundled_path]
34
+ io = io_for_path(to)
35
+ logger.info "bundling to #{to}" if to
36
+ paths.each do |path|
37
+ io << path.read << "\n"
38
+ end
39
+ io
40
+ end
41
+
42
+ def compress!(to = nil)
43
+ to = options[:compressed_path] if to.nil? && options[:compressed_path]
44
+ io = io_for_path(to)
45
+ logger.info "compressing to #{to}"
46
+ io << js_compress(bundle!(false))
47
+ io
48
+ end
49
+
50
+ def vendor!(dir = nil)
51
+ resolve! if paths.empty?
52
+ dir ||= options[:vendor_dir]
53
+ dir ||= 'vendor' # default
54
+ logger.info "vendoring to #{dir}"
55
+ paths.each do |path|
56
+ Jim::Installer.new(path, dir, :shallow => true).install
57
+ end
58
+ end
59
+
60
+ private
61
+ def io_for_path(to)
62
+ case to
63
+ when IO
64
+ to
65
+ when Pathname
66
+ to.dirname.mkpath
67
+ to.open('w')
68
+ when String
69
+ to = Pathname.new(to)
70
+ io_for_path(to)
71
+ else
72
+ ""
73
+ end
74
+ end
75
+
76
+ def parse_jimfile
77
+ jimfile.each_line do |line|
78
+ if /^\/\/\s?([^\:]+)\:\s(.*)$/.match line
79
+ self.options[$1.to_sym] = $2.strip
80
+ elsif line !~ /^\// && line.strip != ''
81
+ self.requirements << line
82
+ end
83
+ end
84
+ end
85
+
86
+ def js_compress(uncompressed)
87
+ if options[:compressor] == 'yui'
88
+ require "yui/compressor"
89
+ compressor = ::YUI::JavaScriptCompressor.new
90
+ else
91
+ require 'closure-compiler'
92
+ compressor = ::Closure::Compiler.new
93
+ end
94
+ compressor.compress(uncompressed)
95
+ end
96
+
97
+ def logger
98
+ Jim.logger
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,143 @@
1
+ module Jim
2
+ class CLI
3
+
4
+ attr_accessor :jimfile, :jimhome, :force
5
+
6
+ def initialize(args)
7
+ @output = ""
8
+ # set the default jimhome
9
+ self.jimhome = Pathname.new(ENV['JIMHOME'] || '~/.jim').expand_path
10
+ # parse the options
11
+ self.jimfile = Pathname.new('Jimfile')
12
+ @args = parse_options(args)
13
+ ## try to run based on args
14
+ end
15
+
16
+ def run
17
+ command = @args.shift
18
+ if command && respond_to?(command)
19
+ self.send(command, *@args)
20
+ elsif command.nil? || command.strip == ''
21
+ cheat
22
+ else
23
+ @output << "No action found for #{command}. Run -h for help."
24
+ end
25
+ @output
26
+ rescue Jim::FileExists => e
27
+ @output << "#{e.message} already exists, bailing. Use --force if you're sure"
28
+ rescue => e
29
+ @output << e.message + " (#{e.class})"
30
+ end
31
+
32
+ def commands
33
+ logger.info "Usage: jim [options] [command] [args]\n"
34
+ logger.info "Commands:"
35
+ logger.info template('commands')
36
+ end
37
+
38
+ def cheat
39
+ logger.info "Usage: jim [options] [command] [args]\n"
40
+ logger.info "Commands:"
41
+ logger.info template('commands').find_all {|l| l.match(/^\w/) }.join("")
42
+ logger.info "run commands for details"
43
+ end
44
+
45
+ def init(dir = nil)
46
+ dir = Pathname.new(dir || '')
47
+ jimfile_path = dir + 'Jimfile'
48
+ if jimfile_path.readable? && !force
49
+ raise Jim::FileExists(jimfile_path)
50
+ else
51
+ File.open(jimfile_path, 'w') do |f|
52
+ f << template('Jimfile')
53
+ end
54
+ logger.info "wrote Jimfile to #{jimfile_path}"
55
+ end
56
+ end
57
+
58
+ def install(url, name = false, version = false)
59
+ Jim::Installer.new(url, jimhome, :force => force, :name => name, :version => version).install
60
+ end
61
+
62
+ def bundle(to = nil)
63
+ path = bundler.bundle!(to)
64
+ end
65
+
66
+ def compress(to = nil)
67
+ path = bundler.compress!(to)
68
+ end
69
+
70
+ def list
71
+ logger.info "Getting list of installed files in #{index.directories.join(':')}"
72
+ list = index.list
73
+ logger.info "Installed:\n#{list.collect {|i| "#{i[0]} (#{i[1].join(', ')})"}.join("\n")}"
74
+ end
75
+
76
+ def resolve
77
+ resolved = bundler.resolve!
78
+ logger.info "Files:\n#{resolved.join("\n")}"
79
+ resolved
80
+ end
81
+
82
+ def vendor(dir = nil)
83
+ bundler.vendor!(dir)
84
+ end
85
+
86
+ private
87
+ def parse_options(runtime_args)
88
+ OptionParser.new("", 24, ' ') do |opts|
89
+ opts.banner = "Usage: jim [options] [command] [args]"
90
+
91
+ opts.separator ""
92
+ opts.separator "jim options:"
93
+
94
+ opts.on("--jimhome path/to/home", "set the install path/JIMHOME dir (default ~/.jim)") {|h|
95
+ self.jimhome = Pathname.new(h)
96
+ }
97
+
98
+ opts.on("-j", "--jimfile path/to/jimfile", "load specific Jimfile at path (default ./Jimfile)") { |j|
99
+ self.jimfile = Pathname.new(j)
100
+ }
101
+
102
+ opts.on("-f", "--force", "force file creation/overwrite") {|f|
103
+ self.force = true
104
+ }
105
+
106
+ opts.on("-d", "--debug", "set log level to debug") {|d|
107
+ logger.level = Logger::DEBUG
108
+ }
109
+
110
+ opts.on("-v", "--version", "print version") {|d|
111
+ puts "jim #{Jim::VERSION}"
112
+ exit
113
+ }
114
+
115
+ opts.on_tail("-h", "--help", "Show this message. Run jim commands for list of commands.") do
116
+ puts opts.help
117
+ exit
118
+ end
119
+
120
+ end.parse! runtime_args
121
+ rescue OptionParser::MissingArgument => e
122
+ logger.warn "#{e}, run -h for options"
123
+ exit
124
+ end
125
+
126
+ def index
127
+ @index ||= Jim::Index.new(jimhome + 'lib')
128
+ end
129
+
130
+ def bundler
131
+ @bundler ||= Jim::Bundler.new(jimfile, index)
132
+ end
133
+
134
+ def template(path)
135
+ (Pathname.new(__FILE__).dirname + 'templates' + path).read
136
+ end
137
+
138
+ def logger
139
+ Jim.logger
140
+ end
141
+
142
+ end
143
+ end