mud 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mud.gemspec
4
+ gemspec
@@ -0,0 +1,27 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mud (0.0.4)
5
+ hpricot (>= 0.8.4)
6
+ sinatra (>= 1.1.2)
7
+ thor (>= 0.14.6)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ hpricot (0.8.4)
13
+ rack (1.2.2)
14
+ sinatra (1.1.2)
15
+ rack (~> 1.1)
16
+ tilt (~> 1.2)
17
+ thor (0.14.6)
18
+ tilt (1.2.2)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ hpricot (>= 0.8.4)
25
+ mud!
26
+ sinatra (>= 1.1.2)
27
+ thor (>= 0.14.6)
@@ -0,0 +1,2 @@
1
+ Ruby port of Mud (package manager for client-side Javascript).
2
+ See [http://github.com/mafintosh/mud](http://github.com/mafintosh/mud) for (non-existing) documentation.
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/mud ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+
5
+ require 'mud'
6
+ require 'mud/cli'
7
+
8
+ begin
9
+ Mud::CLI.start
10
+ rescue Mud::ResolveError => err
11
+ puts err.message, "Try running: mud install #{err.name}"
12
+ rescue Interrupt
13
+ puts "Quitting..."
14
+ end
@@ -0,0 +1,219 @@
1
+ if (typeof JSON === 'undefined') { // Fixed from the original JSON to support Google Closure compilation
2
+ JSON = {};
3
+ }
4
+
5
+ (function () {
6
+ "use strict";
7
+
8
+ function f(n) {
9
+ // Format integers to have at least two digits.
10
+ return n < 10 ? '0' + n : n;
11
+ }
12
+
13
+ if (typeof Date.prototype.toJSON !== 'function') {
14
+
15
+ Date.prototype.toJSON = function (key) {
16
+
17
+ return isFinite(this.valueOf()) ?
18
+ this.getUTCFullYear() + '-' +
19
+ f(this.getUTCMonth() + 1) + '-' +
20
+ f(this.getUTCDate()) + 'T' +
21
+ f(this.getUTCHours()) + ':' +
22
+ f(this.getUTCMinutes()) + ':' +
23
+ f(this.getUTCSeconds()) + 'Z' : null;
24
+ };
25
+
26
+ String.prototype.toJSON =
27
+ Number.prototype.toJSON =
28
+ Boolean.prototype.toJSON = function (key) {
29
+ return this.valueOf();
30
+ };
31
+ }
32
+
33
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
34
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
35
+ gap,
36
+ indent,
37
+ meta = { // table of character substitutions
38
+ '\b': '\\b',
39
+ '\t': '\\t',
40
+ '\n': '\\n',
41
+ '\f': '\\f',
42
+ '\r': '\\r',
43
+ '"' : '\\"',
44
+ '\\': '\\\\'
45
+ },
46
+ rep;
47
+
48
+
49
+ function quote(string) {
50
+
51
+ escapable.lastIndex = 0;
52
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
53
+ var c = meta[a];
54
+ return typeof c === 'string' ? c :
55
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
56
+ }) + '"' : '"' + string + '"';
57
+ }
58
+
59
+
60
+ function str(key, holder) {
61
+
62
+ var i, // The loop counter.
63
+ k, // The member key.
64
+ v, // The member value.
65
+ length,
66
+ mind = gap,
67
+ partial,
68
+ value = holder[key];
69
+
70
+ if (value && typeof value === 'object' &&
71
+ typeof value.toJSON === 'function') {
72
+ value = value.toJSON(key);
73
+ }
74
+
75
+ if (typeof rep === 'function') {
76
+ value = rep.call(holder, key, value);
77
+ }
78
+
79
+ switch (typeof value) {
80
+ case 'string':
81
+ return quote(value);
82
+
83
+ case 'number':
84
+
85
+ return isFinite(value) ? String(value) : 'null';
86
+
87
+ case 'boolean':
88
+ case 'null':
89
+
90
+ return String(value);
91
+
92
+ case 'object':
93
+
94
+ if (!value) {
95
+ return 'null';
96
+ }
97
+
98
+ gap += indent;
99
+ partial = [];
100
+
101
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
102
+
103
+ length = value.length;
104
+ for (i = 0; i < length; i += 1) {
105
+ partial[i] = str(i, value) || 'null';
106
+ }
107
+
108
+ v = partial.length === 0 ? '[]' : gap ?
109
+ '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
110
+ '[' + partial.join(',') + ']';
111
+ gap = mind;
112
+ return v;
113
+ }
114
+
115
+ if (rep && typeof rep === 'object') {
116
+ length = rep.length;
117
+ for (i = 0; i < length; i += 1) {
118
+ if (typeof rep[i] === 'string') {
119
+ k = rep[i];
120
+ v = str(k, value);
121
+ if (v) {
122
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
123
+ }
124
+ }
125
+ }
126
+ } else {
127
+
128
+ for (k in value) {
129
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
130
+ v = str(k, value);
131
+ if (v) {
132
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ v = partial.length === 0 ? '{}' : gap ?
139
+ '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
140
+ '{' + partial.join(',') + '}';
141
+ gap = mind;
142
+ return v;
143
+ }
144
+ }
145
+
146
+ if (typeof JSON.stringify !== 'function') {
147
+ JSON.stringify = function (value, replacer, space) {
148
+
149
+ var i;
150
+ gap = '';
151
+ indent = '';
152
+
153
+ if (typeof space === 'number') {
154
+ for (i = 0; i < space; i += 1) {
155
+ indent += ' ';
156
+ }
157
+
158
+ } else if (typeof space === 'string') {
159
+ indent = space;
160
+ }
161
+
162
+ rep = replacer;
163
+ if (replacer && typeof replacer !== 'function' &&
164
+ (typeof replacer !== 'object' ||
165
+ typeof replacer.length !== 'number')) {
166
+ throw new Error('JSON.stringify');
167
+ }
168
+
169
+ return str('', {'': value});
170
+ };
171
+ }
172
+
173
+ if (typeof JSON.parse !== 'function') {
174
+ JSON.parse = function (text, reviver) {
175
+
176
+ var j;
177
+
178
+ function walk(holder, key) {
179
+
180
+ var k, v, value = holder[key];
181
+ if (value && typeof value === 'object') {
182
+ for (k in value) {
183
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
184
+ v = walk(value, k);
185
+ if (v !== undefined) {
186
+ value[k] = v;
187
+ } else {
188
+ delete value[k];
189
+ }
190
+ }
191
+ }
192
+ }
193
+ return reviver.call(holder, key, value);
194
+ }
195
+
196
+ text = String(text);
197
+ cx.lastIndex = 0;
198
+ if (cx.test(text)) {
199
+ text = text.replace(cx, function (a) {
200
+ return '\\u' +
201
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
202
+ });
203
+ }
204
+
205
+ if (/^[\],:{}\s]*$/
206
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
207
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
208
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
209
+
210
+ j = eval('(' + text + ')');
211
+
212
+ return typeof reviver === 'function' ?
213
+ walk({'': j}, '') : j;
214
+ }
215
+
216
+ throw new SyntaxError('JSON.parse');
217
+ };
218
+ }
219
+ }());
@@ -0,0 +1,10 @@
1
+
2
+ (function(require) {
3
+ <%= render :erb => 'inline_modules.js.erb', :locals => { :modules => modules } %>
4
+
5
+ <% modules.each do |mod| %>
6
+ window[<%= mod.name.to_json %>] = require(<%= mod.name.to_json %>);
7
+ <% end %>
8
+ })();
9
+
10
+ <%= render :erb => 'list.js.erb', :locals => { :list => appends } %>
@@ -0,0 +1,12 @@
1
+
2
+ <%= render :file => 'require.js' %>
3
+
4
+ <%= render :file => 'JSON.js' %>
5
+
6
+ <% modules.each do |mod| %>
7
+ require.define(<%= mod.name.to_json %>, function(module, exports) {
8
+ <%= mod.content %>
9
+ });
10
+ <% end %>
11
+
12
+ <%= render :erb => 'list.js.erb', :locals => { :list => appends } if defined?(appends) %>
@@ -0,0 +1,3 @@
1
+ <% list.each do |item| %>
2
+ <%= item %>
3
+ <% end %>
@@ -0,0 +1,42 @@
1
+ (function() {
2
+ if (typeof require !== 'undefined') {
3
+ return; // define only once
4
+ }
5
+
6
+ var noop = function() {};
7
+ var modules = {};
8
+ var definitions = {};
9
+
10
+ require = function(name) {
11
+ if (arguments.length > 1) { // this syntax allows for and module and it's plugins to be loaded
12
+ var val = require(arguments[0]);
13
+
14
+ for (var i = 1; i < arguments.length; i++) {
15
+ require(arguments[i]);
16
+ }
17
+ return val;
18
+ }
19
+ name = name.split('@')[0]; // TODO: make this versioning a lot better
20
+
21
+ if (definitions[name] && !modules[name]) { // if not already loaded and an def exists
22
+ var def = definitions[name];
23
+
24
+ delete definitions[name];
25
+
26
+ var module = modules[name] = function() {
27
+ return module.exports;
28
+ };
29
+
30
+ module.browser = true; // allows for non-hacky browser js detection
31
+ module.exports = {};
32
+
33
+ def(module, module.exports);
34
+ }
35
+
36
+ return window[name] || (modules[name] || noop)();
37
+ };
38
+
39
+ require.define = function(name, def) {
40
+ definitions[name] = def;
41
+ };
42
+ }());
@@ -0,0 +1,18 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+ require 'erb'
7
+ require 'rbconfig'
8
+
9
+ require 'sinatra/base'
10
+ require 'thor'
11
+ require 'hpricot'
12
+
13
+ module Mud
14
+ require 'mud/utils'
15
+ extend Mud::Utils
16
+ end
17
+
18
+ %w(context dependency js_result html_result module installed_module server).each { |f| require "mud/#{f}" }
@@ -0,0 +1,65 @@
1
+ module Mud
2
+
3
+ class CLI < Thor
4
+ include Thor::Actions
5
+
6
+ def initialize(*)
7
+ super
8
+ @context = Mud::Context.new
9
+ say("(in #{@context.dir})")
10
+ end
11
+
12
+ map "ls" => "list"
13
+
14
+ desc "resolve PATH", "resolve the given path"
15
+ method_option :compile, :default => nil, :desc => "compile loaded modules"
16
+ def resolve(path)
17
+ modules = @context.resolve_document(path)
18
+ result = Mud::JsResult.new(modules, :compile => options[:compile])
19
+ say(result.to_s)
20
+ end
21
+
22
+ desc "inline PATH", "resolve and inline the given path"
23
+ method_option :compile, :default => nil, :desc => "compile loaded modules"
24
+ def inline(path)
25
+ result = @context.inline_document(path, :compile => options[:compile])
26
+ say(result.to_s)
27
+ end
28
+
29
+ desc "module A,B,...,C", "load in these modules"
30
+ method_option :compile, :default => nil, :desc => "compile loaded modules"
31
+ #method_option :output, :default => nil, :desc => "output file"
32
+ def modules(modules)
33
+ modules = modules.split(',').map { |mod_name| @context.module!(mod_name) }
34
+ result = @context.inline(modules, :compile => options[:compile])
35
+
36
+ if out = options[:output]
37
+ File.open(out, 'w') { |f| f.write(result.to_s) }
38
+ else
39
+ say(result.to_s)
40
+ end
41
+ end
42
+
43
+ desc "server", "run a mud server"
44
+ #method_options :fork, :default => false, :desc => "run the server as a daemon (only supported on unix platforms)"
45
+ def server
46
+ Mud::Server.context = @context
47
+ Mud::Server.run!
48
+ end
49
+
50
+ desc "list", "list all installed packages"
51
+ method_option :path, :default => false, :desc => "include module paths"
52
+ def list
53
+ modules = @context.available_modules.values
54
+ if modules.empty?
55
+ say("no modules found")
56
+ else
57
+ modules.each do |m|
58
+ msg = m.name + (options[:path] ? " => #{m.path}" : '')
59
+ say(msg)
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ end
@@ -0,0 +1,167 @@
1
+ module Mud
2
+
3
+ class ResolveError < StandardError
4
+ attr_reader :name
5
+
6
+ def initialize(name_or_dependency)
7
+ @name = name_or_dependency.is_a?(Mud::Dependency) ? name_or_dependency.name : name_or_dependency
8
+ super("No module named '#{@name}' in context")
9
+ end
10
+ end
11
+
12
+ class Context
13
+ MODULE_DIRECTORIES = ['js_modules', 'shared_modules']
14
+ MODULE_GLOBAL = Mud.home_directory('.mud', 'js_modules')
15
+
16
+ attr_reader :available_modules, :dir
17
+
18
+ def initialize(dir = '.')
19
+ @dir = File.absolute_path(dir)
20
+ @available_modules = {}
21
+ reload
22
+ end
23
+
24
+ def reload
25
+ dirs = dirs(@dir)
26
+ removed = @available_modules.dup
27
+
28
+ dirs.each do |dir|
29
+ Dir.glob(File.join(dir, '*.js')) do |mod_path|
30
+ name = Mud::Module.parse_name(mod_path)
31
+ mod = @available_modules[name]
32
+ removed.delete(name)
33
+
34
+ unless mod and mod.modified == File.mtime(mod_path) and mod.path == mod_path
35
+ @available_modules[name] = Mud::InstalledModule.new(mod_path, self)
36
+ end
37
+ end
38
+ end
39
+
40
+ @available_modules.delete_if { |key, _| removed.key?(key) }
41
+ end
42
+
43
+ def install(name, opts = {})
44
+ return if @available_modules[name]# or raise exception
45
+
46
+ src = nil # download module src from mudhub and write to disk in module global
47
+ path = nil
48
+
49
+ @available_modules[name] = Mud::InstalledModule.new(path, self) # check dependencies and download if not present
50
+ end
51
+
52
+ def uninstall(module_or_name)
53
+ end
54
+
55
+ def module(name)
56
+ @available_modules[name]
57
+ end
58
+
59
+ def module!(name)
60
+ @available_modules[name] || (raise Mud::ResolveError.new(name))
61
+ end
62
+
63
+ def resolve_document(path)
64
+ resolve analyze_document(path).first
65
+ end
66
+
67
+ def inline_document(path, opts = {})
68
+ modules, type = analyze_document(path)
69
+
70
+ result = inline(modules, opts)
71
+
72
+ if type == :js
73
+ main = modules.first
74
+ result << main.content
75
+ else
76
+ result = Mud::HtmlResult.new(path, result)
77
+ end
78
+
79
+ result
80
+ end
81
+
82
+ def resolve(module_or_list)
83
+ modules = module_or_list.is_a?(Mud::Module) ? [module_or_list] : module_or_list
84
+
85
+ resolved = []
86
+
87
+ resolver = proc do |modules|
88
+ modules.each do |mod|
89
+ next if resolved.include?(mod)
90
+ resolved.unshift(mod) if mod.is_a?(Mud::InstalledModule)
91
+
92
+ dep = mod.unresolvable_dependencies.first
93
+ raise Mud::ResolveError.new(dep) if dep
94
+
95
+ dependencies = mod.dependencies.map(&:resolve).delete_if { |m| resolved.include?(m) }
96
+ resolver.call(dependencies) unless dependencies.empty?
97
+ end
98
+ end
99
+ resolver.call(modules)
100
+
101
+ resolved
102
+ end
103
+
104
+ def inline(module_or_list, opts = {})
105
+ resolved = resolve(module_or_list)
106
+ Mud::JsResult.new(resolved, opts)
107
+ end
108
+
109
+ private
110
+
111
+ def analyze_document(path)
112
+ content = Mud.render path
113
+ type = content.match(/^\s*</) ? :html : :js
114
+
115
+ modules = if type == :html
116
+ analyze_html(path, content)
117
+ else
118
+ [Mud::Module.new(path, content, self)]
119
+ end
120
+
121
+ return modules, type
122
+ end
123
+
124
+ def analyze_html(path, html)
125
+ inner_modules = []
126
+ doc = Hpricot(html)
127
+
128
+ doc.search('//script').each do |script_tag|
129
+ src = script_tag.attributes['src']
130
+
131
+ if src and not src.empty? and not src.match(/^\w+:\/\//)
132
+ begin
133
+ content = Mud.render src, :basepath => path
134
+ inner_modules << Mud::Module.new(src, content, self)
135
+ rescue Errno::ENOENT, Net::HTTPError
136
+ # Does not exist. Ignore.
137
+ end
138
+ end
139
+
140
+ content = script_tag.inner_html
141
+ if content and not content.empty?
142
+ inner_modules << Mud::Module.new("#{File.basename(path)}-embedded-script-#{inner_modules.length}", content, self)
143
+ end
144
+ end
145
+
146
+ inner_modules
147
+ end
148
+
149
+ def dirs(start)
150
+ dirs = [MODULE_GLOBAL]
151
+
152
+ current = File.absolute_path(start)
153
+ while true
154
+ dirs += MODULE_DIRECTORIES.map { |dir| File.join(current, dir) }
155
+ break if current == Mud.root_directory
156
+ current = File.expand_path(current, '..')
157
+ end
158
+
159
+ dirs.keep_if { |dir| File.exists?(dir) }
160
+ end
161
+
162
+ def in(*paths)
163
+ File.join(@dir, *paths)
164
+ end
165
+ end
166
+
167
+ end
@@ -0,0 +1,40 @@
1
+ module Mud
2
+
3
+ class Dependency
4
+ def self.analyze(src, context)
5
+ dependencies = Set.new
6
+
7
+ # Find all required files on the form require('name') og require('name', 'sub_name', ...)
8
+ src.scan(/require\(((?:'[^']+'(?:,\s)?)+)\)/).each do |req|
9
+ req.first.scan(/'([^']+)'/).each { |name| dependencies << new(name.first, context) }
10
+ end
11
+
12
+ dependencies.to_a
13
+ end
14
+
15
+ attr_reader :name, :context
16
+
17
+ def initialize(name, context)
18
+ @name = name
19
+ @context = context
20
+ end
21
+
22
+ def to_s
23
+ "Mud::Dependency #{@name}"
24
+ end
25
+
26
+ def ==(other)
27
+ other.is_a?(self.class) and other.name == @name
28
+ end
29
+
30
+ def resolvable?
31
+ !!resolve
32
+ end
33
+
34
+ # Resolves to the corresponding module if present in this context.
35
+ def resolve
36
+ @context.module(@name)
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,39 @@
1
+ module Mud
2
+
3
+ class HtmlResult
4
+ def initialize(html, js)
5
+ @html = html
6
+ @js = js
7
+ end
8
+
9
+ def to_s
10
+ doc = html_doc
11
+ script = doc.search('//script').find { |script_tag| /.*\/dev$/.match script_tag.attributes['src'] }
12
+
13
+ if script
14
+ script.remove_attribute(:src)
15
+ else
16
+ script = Hpricot::Elem.new('script', :type => 'text/javascript')
17
+ head = doc.at('/html/head')
18
+
19
+ unless head
20
+ head = Hpricot::Elem.new('head')
21
+ doc.root.children.unshift(head)
22
+ end
23
+
24
+ head.children = (head.children || []).unshift(script)
25
+ end
26
+
27
+ script.inner_html = @js.to_s
28
+
29
+ doc.to_html
30
+ end
31
+
32
+ private
33
+
34
+ def html_doc
35
+ Hpricot(Mud.render @html)
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,19 @@
1
+ module Mud
2
+
3
+ class InstalledModule < Mud::Module
4
+ attr_reader :path, :modified
5
+
6
+ def initialize(path, context)
7
+ super(path, File.open(path) { |f| f.read }, context)
8
+ @content = nil
9
+
10
+ @path = path
11
+ @modified = File.mtime(path)
12
+ end
13
+
14
+ def content
15
+ File.open(@path) { |f| f.read }
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,25 @@
1
+ module Mud
2
+
3
+ class JsResult
4
+ def initialize(modules, opts = {})
5
+ opts = { :global => false, :compile => nil }.update(opts)
6
+
7
+ @modules = modules
8
+ @global = opts[:global]
9
+ @compile = opts[:compile]
10
+
11
+ @appends = []
12
+ end
13
+
14
+ def to_s
15
+ result = Mud.render :erb => (@global ? 'global.js.erb' : 'inline_modules.js.erb'),
16
+ :locals => { :modules => @modules, :appends => @appends }, :basepath => Mud.js_directory
17
+ @compile ? Mud.compile(result, @compile) : result
18
+ end
19
+
20
+ def <<(src)
21
+ @appends << src
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,39 @@
1
+ module Mud
2
+
3
+ class Module
4
+ attr_reader :name, :content, :context, :dependencies
5
+
6
+ def self.parse_name(path_or_name)
7
+ File.basename(path_or_name).split(/\.js$/i).first
8
+ end
9
+
10
+ def initialize(name, content, context)
11
+ @name = self.class.parse_name(name)
12
+ @content = content
13
+ @context = context
14
+
15
+ @dependencies = Mud::Dependency.analyze(content, context)
16
+ end
17
+
18
+ def to_s
19
+ "#{self.class} #{@name}"
20
+ end
21
+
22
+ def ==(other)
23
+ other.is_a?(self.class) and other.name == @name
24
+ end
25
+
26
+ def unresolvable_dependencies
27
+ @dependencies.select { |d| not d.resolvable? }
28
+ end
29
+
30
+ def resolvable?
31
+ @dependencies.all?(&:resolvable?)
32
+ end
33
+
34
+ def depends_on?(module_or_dependency)
35
+ !!@dependencies.find { |d| d.name == module_or_dependency.name }
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,86 @@
1
+ module Mud
2
+
3
+ class Server < Sinatra::Base
4
+ enable :logging, :dump_errors, :inline_templates
5
+ disable :static, :run
6
+
7
+ set :port, 10000
8
+
9
+ class << self
10
+ def port=(port)
11
+ set :port, port
12
+ end
13
+
14
+ def context=(context)
15
+ @@context = context
16
+ end
17
+ end
18
+
19
+ helpers do
20
+ def context
21
+ unless defined?(@@context)
22
+ @@context = Mud::Context.new
23
+ else
24
+ @@context.reload
25
+ end
26
+
27
+ @@context
28
+ end
29
+
30
+ def js
31
+ content_type 'application/javascript'
32
+ end
33
+
34
+ def process_modules(modules, opts)
35
+ halt 400 if modules.nil? or modules.empty?
36
+ modules = modules.split(',').map { |name| context.module!(name) }
37
+ context.inline(modules, opts).to_s
38
+ end
39
+ end
40
+
41
+ get '/dev' do
42
+ ref = params[:ref] || request.referrer
43
+
44
+ js
45
+ if ref.nil? or ref == '/'
46
+ host = "#{request.host.split(':').first}:#{settings.port}"
47
+ erb :dev, :locals => { :host => host }
48
+ else
49
+ modules = context.resolve_document(ref)
50
+ Mud::JsResult.new(modules).to_s
51
+ end
52
+ end
53
+
54
+ get '/m/:modules' do |modules|
55
+ js
56
+ process_modules(modules, :global => false)
57
+ end
58
+
59
+ get '/g/:modules' do |modules|
60
+ js
61
+ process_modules(modules, :global => true)
62
+ end
63
+
64
+ get '/p/:var' do |var|
65
+ content_type 'text/html'
66
+ erb :play, :locals => { :var => var }
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ __END__
73
+
74
+ @@ dev
75
+ document.write('<script src="http://<%= host %>/dev?ref=' + window.location + '"></script>');
76
+
77
+ @@ play
78
+ <!DOCTYPE html>
79
+ <html>
80
+ <head>
81
+ <title>mud play</title>
82
+ <script src="<%= "/m/#{var}" %>"></script>
83
+ </head>
84
+ <body>
85
+ </body>
86
+ </html>
@@ -0,0 +1,146 @@
1
+ module Mud
2
+
3
+ File.class_eval do
4
+ def self.hide(file_name, force_rename = false)
5
+ os = RbConfig::CONFIG['host_os']
6
+
7
+ if os =~ /mswin|windows|cygwin/i
8
+ raise IOError.new("Could not hide file '#{file_name}' using attrib +h") unless system("attrib +h '#{file_name}'")
9
+ file_name
10
+ else
11
+ # Assume unix-like
12
+
13
+ parent, base = dirname(file_name), basename(file_name)
14
+
15
+ if base.start_with?('.')
16
+ file_name
17
+ else
18
+ if os =~ /darwin/i and not force_rename
19
+ # OS X
20
+ raise IOError.new("Could not hide file '#{file_name}' using SetFile -a -V") unless system("SetFile -a V '#{file_name}'")
21
+ return file_name
22
+ end
23
+
24
+ hidden = join(parent, ".#{base}")
25
+ raise IOError.new("Can't hide '#{file_name}' by renaming to '#{hidden} (already exists)") if exists?(hidden)
26
+ rename(file_name, hidden)
27
+ hidden
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ module Utils
34
+ JS_DIRECTORY = File.expand_path(File.join File.dirname(__FILE__), '..', '..', 'js')
35
+
36
+ ROOT_DIRECTORY = File.absolute_path('/')
37
+ HOME_DIRECTORY = Dir.home
38
+
39
+ [:js, :root, :home].each do |name|
40
+ name = "#{name}_directory"
41
+ module_eval %{
42
+ def #{name}(*paths)
43
+ File.join(#{name.upcase}, *paths)
44
+ end
45
+ }
46
+ end
47
+
48
+ def compile(src, type = 'simple', out = nil)
49
+ raise ArgumentError.new("Type must be either 'simple' or 'advanced', was '#{type}'") unless ['simple', 'advanced'].include?(type.to_s)
50
+ level = "#{type.upcase}_OPTIMIZATIONS"
51
+
52
+ response = Net::HTTP.post_form(URI.parse('http://closure-compiler.appspot.com/compile'),
53
+ :output_info => 'compiled_code',
54
+ :compilation_level => level,
55
+ :warning_level => 'default',
56
+ :js_code => src)
57
+
58
+ if out
59
+ File.open(out) { |f| f.write(response.body) }
60
+ end
61
+
62
+ response.body
63
+ end
64
+
65
+ def render(location, opts = {})
66
+ if location.is_a?(Hash)
67
+ opts = location
68
+ else
69
+ opts = guess(location).update(opts)
70
+ end
71
+
72
+ type, path = opts.first
73
+
74
+ content = case type
75
+ when :erb, :file then
76
+ basepath = opts[:basepath] || path
77
+ basepath = basepath.gsub(/^file:\/\//, '')
78
+
79
+ path = opts[:basepath] ? File.join(basepath, path) : basepath
80
+
81
+ File.open(path) { |f| f.read }
82
+ when :http then
83
+ basepath = opts[:basepath] || path
84
+ basepath = "http://#{basepath}" unless basepath.start_with?('http://')
85
+
86
+ path = opts[:basepath] ? URI.join(basepath, path) : basepath
87
+
88
+ response = Net::HTTP.get_response(URI.parse(path))
89
+ response.error! unless (200..299).include?(response.code.to_i)
90
+ response.body
91
+ else
92
+ raise ArgumentError.new("Unknown type '#{type}'")
93
+ end
94
+
95
+ if type == :erb
96
+ locals = opts[:locals] || {}
97
+ content = ERB.new(content).result(LocalsBinding.new(locals).binding)
98
+ end
99
+
100
+ content
101
+ end
102
+ alias :cat :render
103
+
104
+ private
105
+
106
+ class LocalsBinding < BasicObject
107
+ def initialize(locals, &block)
108
+ @_locals = locals
109
+
110
+ locals.each_pair do |name, value|
111
+ instance_eval %{
112
+ def #{name}
113
+ _get(:#{name})
114
+ end
115
+ }
116
+ end
117
+
118
+ instance_eval(&block) if block
119
+ end
120
+
121
+ def js_directory(*paths)
122
+ ::Mud.js_directory(*paths)
123
+ end
124
+
125
+ def render(opts)
126
+ ::Mud.render(opts.update :basepath => js_directory)
127
+ end
128
+
129
+ def binding
130
+ ::Proc.new {}.binding
131
+ end
132
+
133
+ private
134
+
135
+ def _get(name)
136
+ @_locals[name.to_sym] || @_locals[name.to_s]
137
+ end
138
+ end
139
+
140
+ def guess(path)
141
+ protocol = (path.match(/^(\w+):\/\//) || [])[1] || 'file'
142
+ { protocol.to_sym => path }
143
+ end
144
+ end
145
+
146
+ end
@@ -0,0 +1,3 @@
1
+ module Mud
2
+ VERSION = "0.0.4"
3
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mud/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "mud"
7
+ s.version = Mud::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Mirza Kapetanovic", "Mathias Buus"]
10
+ #s.email = [""]
11
+ s.homepage = "http://mudhub.org"
12
+ s.summary = %q{Simple browser Javascript package manager}
13
+ s.description = %q{Mud is a simple package manager for client-side Javascript. Used for installing new packages and resolving dependencies.}
14
+
15
+ #s.rubyforge_project = "mud"
16
+
17
+ s.add_dependency("sinatra", ">= 1.1.2")
18
+ s.add_dependency("thor", ">= 0.14.6")
19
+ s.add_dependency("hpricot", ">= 0.8.4")
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ #s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = ["mud"]
24
+ s.require_paths = ["lib"]
25
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mud
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 4
9
+ version: 0.0.4
10
+ platform: ruby
11
+ authors:
12
+ - Mirza Kapetanovic
13
+ - Mathias Buus
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-05-29 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: sinatra
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ segments:
30
+ - 1
31
+ - 1
32
+ - 2
33
+ version: 1.1.2
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: thor
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ segments:
45
+ - 0
46
+ - 14
47
+ - 6
48
+ version: 0.14.6
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: hpricot
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ segments:
60
+ - 0
61
+ - 8
62
+ - 4
63
+ version: 0.8.4
64
+ type: :runtime
65
+ version_requirements: *id003
66
+ description: Mud is a simple package manager for client-side Javascript. Used for installing new packages and resolving dependencies.
67
+ email:
68
+ executables:
69
+ - mud
70
+ extensions: []
71
+
72
+ extra_rdoc_files: []
73
+
74
+ files:
75
+ - .gitignore
76
+ - Gemfile
77
+ - Gemfile.lock
78
+ - README.md
79
+ - Rakefile
80
+ - bin/mud
81
+ - js/JSON.js
82
+ - js/global.js.erb
83
+ - js/inline_modules.js.erb
84
+ - js/list.js.erb
85
+ - js/require.js
86
+ - lib/mud.rb
87
+ - lib/mud/cli.rb
88
+ - lib/mud/context.rb
89
+ - lib/mud/dependency.rb
90
+ - lib/mud/html_result.rb
91
+ - lib/mud/installed_module.rb
92
+ - lib/mud/js_result.rb
93
+ - lib/mud/module.rb
94
+ - lib/mud/server.rb
95
+ - lib/mud/utils.rb
96
+ - lib/mud/version.rb
97
+ - mud.gemspec
98
+ has_rdoc: true
99
+ homepage: http://mudhub.org
100
+ licenses: []
101
+
102
+ post_install_message:
103
+ rdoc_options: []
104
+
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ segments:
121
+ - 0
122
+ version: "0"
123
+ requirements: []
124
+
125
+ rubyforge_project:
126
+ rubygems_version: 1.3.7
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: Simple browser Javascript package manager
130
+ test_files: []
131
+