jsus 0.2.7 → 0.3.0
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.
- data/.travis.yml +6 -0
- data/CHANGELOG +4 -1
- data/Gemfile +4 -1
- data/VERSION +1 -1
- data/bin/jsus +30 -9
- data/features/command-line/compression.feature +24 -0
- data/features/command-line/mooforge_compatibility_layer.feature +75 -0
- data/features/data/Compression/Source/Library/Color.js +17 -0
- data/features/data/Compression/Source/Widget/Input/Input.Color.js +21 -0
- data/features/data/Compression/package.yml +9 -0
- data/features/data/MooforgePlugin/Core/Source/Core.js +16 -0
- data/features/data/MooforgePlugin/Core/package.yml +8 -0
- data/features/data/MooforgePlugin/Plugin/Source/plugin-support.js +20 -0
- data/features/data/MooforgePlugin/Plugin/Source/plugin.js +21 -0
- data/features/data/MooforgePlugin/Plugin/package.yml +9 -0
- data/features/data/compression.min.js +1 -0
- data/jsus.gemspec +45 -22
- data/lib/jsus/middleware.rb +178 -0
- data/lib/jsus/package.rb +10 -10
- data/lib/jsus/source_file.rb +58 -30
- data/lib/jsus/tag.rb +18 -10
- data/lib/jsus/util/file_cache.rb +57 -0
- data/lib/jsus/util/inflection.rb +39 -0
- data/lib/jsus/util.rb +2 -0
- data/lib/jsus.rb +2 -0
- data/spec/data/ComplexDependencies/Mootools/Source/Core.js +16 -0
- data/spec/data/ComplexDependencies/Mootools/package.yml +8 -0
- data/spec/data/ComplexDependencies/Output/package.js +70 -0
- data/spec/data/ComplexDependencies/Output/scripts.json +14 -0
- data/spec/data/ComplexDependencies/Output/tree.json +33 -0
- data/spec/data/ComplexDependencies/Source/Library/Color.js +19 -0
- data/spec/data/ComplexDependencies/Source/Widget/Input/Input.Color.js +20 -0
- data/spec/data/ComplexDependencies/Source/Widget/Input.js +16 -0
- data/spec/data/ComplexDependencies/package.yml +10 -0
- data/spec/data/mooforge_quirky_source.js +20 -0
- data/spec/data/unicode_source.js +23 -0
- data/spec/data/unicode_source_with_bom.js +23 -0
- data/spec/jsus/middleware_spec.rb +216 -0
- data/spec/jsus/package_spec.rb +12 -6
- data/spec/jsus/source_file_spec.rb +42 -14
- data/spec/jsus/tag_spec.rb +8 -2
- data/spec/jsus/util/file_cache_spec.rb +67 -0
- data/spec/jsus/util/inflection_spec.rb +42 -0
- metadata +124 -62
- data/Gemfile.lock +0 -79
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
module Jsus
|
3
|
+
#
|
4
|
+
# Rack middleware
|
5
|
+
#
|
6
|
+
# Just "use Jsus::Middleware" in your rack application and all the requests
|
7
|
+
# to /javascripts/jsus/* are redirected to the middleware.
|
8
|
+
#
|
9
|
+
# Configuration:
|
10
|
+
#
|
11
|
+
# Use Jsus::Middleware.settings= method to change some settings, such as:
|
12
|
+
# :packages_dir - path to where you store your packages
|
13
|
+
# :cache - enable simple file caching
|
14
|
+
# :cache_path - directory for file caching
|
15
|
+
# :prefix - change /jsus/ to something else or remove it altogether (set to nil)
|
16
|
+
# :cache_pool - cache js pool between requests. Can save you some time
|
17
|
+
# between requests but annoys a lot during development.
|
18
|
+
#
|
19
|
+
#
|
20
|
+
# Examples:
|
21
|
+
#
|
22
|
+
# GET /javascripts/jsus/Mootools.Core+Mootools.More
|
23
|
+
# merges packages named Mootools.Core and Mootools.More with all the
|
24
|
+
# dependencies and outputs the result.
|
25
|
+
#
|
26
|
+
# GET /javascripts/jsus/Mootools.More~Mootools.Core
|
27
|
+
# returns package Mootools.More with all the dependencies MINUS any of
|
28
|
+
# Mootools.Core dependencies.
|
29
|
+
#
|
30
|
+
# GET /javascripts/jsus/Mootools.Core:Class+Mootools.More:Fx
|
31
|
+
# same thing but for source files providing Mootools.Core/Class and
|
32
|
+
# Mootools.More/Fx
|
33
|
+
#
|
34
|
+
#
|
35
|
+
# Also see sinatra example https://github.com/jsus/jsus-sinatra-app
|
36
|
+
#
|
37
|
+
class Middleware
|
38
|
+
include Rack
|
39
|
+
class <<self
|
40
|
+
DEFAULT_SETTINGS = {
|
41
|
+
:packages_dir => ".",
|
42
|
+
:cache => false,
|
43
|
+
:cache_path => nil,
|
44
|
+
:prefix => "jsus",
|
45
|
+
:cache_pool => true
|
46
|
+
}.freeze
|
47
|
+
|
48
|
+
def settings
|
49
|
+
@settings ||= DEFAULT_SETTINGS.dup
|
50
|
+
end # settings
|
51
|
+
|
52
|
+
def settings=(new_settings)
|
53
|
+
settings.merge!(new_settings)
|
54
|
+
end # settings=
|
55
|
+
|
56
|
+
def pool
|
57
|
+
@pool ||= Jsus::Pool.new(settings[:packages_dir])
|
58
|
+
end # pool
|
59
|
+
|
60
|
+
def cache?
|
61
|
+
settings[:cache]
|
62
|
+
end # cache?
|
63
|
+
|
64
|
+
def cache
|
65
|
+
@cache ||= cache? ? Util::FileCache.new(settings[:cache_path]) : nil
|
66
|
+
end # cache
|
67
|
+
end # class <<self
|
68
|
+
|
69
|
+
def initialize(app)
|
70
|
+
@app = app
|
71
|
+
end # initialize
|
72
|
+
|
73
|
+
def _call(env)
|
74
|
+
path = Utils.unescape(env["PATH_INFO"])
|
75
|
+
return @app.call(env) unless handled_by_jsus?(path)
|
76
|
+
path.sub!(path_prefix_regex, "")
|
77
|
+
components = path.split("/")
|
78
|
+
return @app.call(env) unless components.size >= 2
|
79
|
+
if components[0] == "require"
|
80
|
+
generate(components[1])
|
81
|
+
else
|
82
|
+
not_found!
|
83
|
+
end
|
84
|
+
end # _call
|
85
|
+
|
86
|
+
def call(env)
|
87
|
+
dup._call(env)
|
88
|
+
end # call
|
89
|
+
|
90
|
+
protected
|
91
|
+
|
92
|
+
def generate(path_string)
|
93
|
+
path_args = parse_path_string(path_string.sub(/.js$/, ""))
|
94
|
+
files = []
|
95
|
+
path_args[:include].each {|tag| files += get_associated_files(tag).to_a }
|
96
|
+
path_args[:exclude].each {|tag| files -= get_associated_files(tag).to_a }
|
97
|
+
if !files.empty?
|
98
|
+
response = Container.new(*files).map {|f| f.content }.join("\n")
|
99
|
+
cache.write(path_string, response) if cache?
|
100
|
+
respond_with(response)
|
101
|
+
else
|
102
|
+
not_found!
|
103
|
+
end
|
104
|
+
end # generate
|
105
|
+
|
106
|
+
# Notice: + is a space after url decoding
|
107
|
+
# input:
|
108
|
+
# "Package:A~Package:C Package:B~Other:D"
|
109
|
+
# output:
|
110
|
+
# {:include => ["Package/A", "Package/B"], :exclude => ["Package/C", "Other/D"]}
|
111
|
+
def parse_path_string(path_string)
|
112
|
+
path_string = " " + path_string unless path_string[0,1] =~ /\+\-/
|
113
|
+
included = []
|
114
|
+
excluded = []
|
115
|
+
path_string.scan(/([ ~])([^ ~]*)/) do |op, arg|
|
116
|
+
arg = arg.gsub(":", "/")
|
117
|
+
if op == " "
|
118
|
+
included << arg
|
119
|
+
else
|
120
|
+
excluded << arg
|
121
|
+
end
|
122
|
+
end
|
123
|
+
{:include => included, :exclude => excluded}
|
124
|
+
end # parse_path_string
|
125
|
+
|
126
|
+
def get_associated_files(source_file_or_package)
|
127
|
+
if package = pool.packages.detect {|pkg| pkg.name == source_file_or_package}
|
128
|
+
package.include_dependencies!
|
129
|
+
package.linked_external_dependencies.to_a + package.source_files.to_a
|
130
|
+
elsif source_file = pool.lookup(source_file_or_package)
|
131
|
+
pool.lookup_dependencies(source_file) << source_file
|
132
|
+
else
|
133
|
+
[]
|
134
|
+
end
|
135
|
+
end # get_associated_files
|
136
|
+
|
137
|
+
def not_found!
|
138
|
+
[404, {"Content-Type" => "text/plain"}, ["Jsus doesn't know anything about this entity"]]
|
139
|
+
end # not_found!
|
140
|
+
|
141
|
+
def respond_with(text)
|
142
|
+
[200, {"Content-Type" => "text/javascript"}, [text]]
|
143
|
+
end # respond_with
|
144
|
+
|
145
|
+
|
146
|
+
def handled_by_jsus?(path)
|
147
|
+
path =~ path_prefix_regex
|
148
|
+
end # handled_by_jsus?
|
149
|
+
|
150
|
+
def path_prefix
|
151
|
+
@path_prefix ||= self.class.settings[:prefix] ? "/javascripts/#{self.class.settings[:prefix]}/" : "/javascripts/"
|
152
|
+
end # path_prefix
|
153
|
+
|
154
|
+
def path_prefix_regex
|
155
|
+
@path_prefix_regex ||= %r{^#{path_prefix}}
|
156
|
+
end # path_prefix_regex
|
157
|
+
|
158
|
+
def pool
|
159
|
+
if cache_pool?
|
160
|
+
self.class.pool
|
161
|
+
else
|
162
|
+
@pool ||= Jsus::Pool.new(self.class.settings[:packages_dir])
|
163
|
+
end
|
164
|
+
end # pool
|
165
|
+
|
166
|
+
def cache?
|
167
|
+
self.class.cache?
|
168
|
+
end # cache?
|
169
|
+
|
170
|
+
def cache
|
171
|
+
self.class.cache
|
172
|
+
end # cache
|
173
|
+
|
174
|
+
def cache_pool?
|
175
|
+
self.class.settings[:cache_pool]
|
176
|
+
end # cache_pool?
|
177
|
+
end # class Middleware
|
178
|
+
end # module Jsus
|
data/lib/jsus/package.rb
CHANGED
@@ -7,7 +7,7 @@ module Jsus
|
|
7
7
|
attr_accessor :directory # directory which this package resides in (full path)
|
8
8
|
attr_accessor :pool # an instance of Pool
|
9
9
|
# Constructors
|
10
|
-
|
10
|
+
|
11
11
|
#
|
12
12
|
# Creates a package from given directory.
|
13
13
|
#
|
@@ -48,7 +48,7 @@ module Jsus
|
|
48
48
|
|
49
49
|
|
50
50
|
# Public API
|
51
|
-
|
51
|
+
|
52
52
|
# Returns a package.yml header.
|
53
53
|
def header
|
54
54
|
@header ||= {}
|
@@ -58,15 +58,15 @@ module Jsus
|
|
58
58
|
def name
|
59
59
|
header["name"] ||= ""
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
# Returns a package description.
|
63
63
|
def description
|
64
64
|
header["description"] ||= ""
|
65
|
-
end
|
65
|
+
end
|
66
66
|
|
67
67
|
# Returns a filename for compiled package.
|
68
68
|
def filename
|
69
|
-
header["filename"] ||= name + ".js"
|
69
|
+
header["filename"] ||= Jsus::Util::Inflection.snake_case(name) + ".js"
|
70
70
|
end
|
71
71
|
|
72
72
|
# Returns a list of sources filenames.
|
@@ -81,7 +81,7 @@ module Jsus
|
|
81
81
|
end
|
82
82
|
|
83
83
|
# Returns an array of provided tags names including those provided by linked external dependencies.
|
84
|
-
def provides_names
|
84
|
+
def provides_names
|
85
85
|
source_files.map {|s| s.provides_names(:short => true) }.flatten |
|
86
86
|
linked_external_dependencies.map {|d| d.provides_names }.flatten
|
87
87
|
end
|
@@ -103,7 +103,7 @@ module Jsus
|
|
103
103
|
def external_dependencies
|
104
104
|
source_files.map {|s| s.external_dependencies }.flatten
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
# Returns an array of external dependencies' names (including resolved ones).
|
108
108
|
def external_dependencies_names
|
109
109
|
external_dependencies.map {|d| d.name }
|
@@ -113,7 +113,7 @@ module Jsus
|
|
113
113
|
def linked_external_dependencies
|
114
114
|
@linked_external_dependencies ||= Container.new
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
# Compiles source files and linked external source files into a given category.
|
118
118
|
def compile(directory = ".")
|
119
119
|
fn = directory ? File.join(directory, filename) : nil
|
@@ -164,7 +164,7 @@ module Jsus
|
|
164
164
|
end
|
165
165
|
|
166
166
|
# Lists the required files for the package.
|
167
|
-
def required_files
|
167
|
+
def required_files
|
168
168
|
source_files.map {|s| s.required_files }.flatten
|
169
169
|
end
|
170
170
|
|
@@ -189,7 +189,7 @@ module Jsus
|
|
189
189
|
end
|
190
190
|
|
191
191
|
# Private API
|
192
|
-
|
192
|
+
|
193
193
|
def header=(new_header) # :nodoc:
|
194
194
|
@header = new_header
|
195
195
|
end
|
data/lib/jsus/source_file.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Jsus
|
2
2
|
# Generic exception for 'bad' source files (no yaml header, for example)
|
3
3
|
class BadSourceFileException < Exception; end
|
4
|
-
|
4
|
+
|
5
5
|
#
|
6
6
|
# SourceFile is a base for any Jsus operation.
|
7
|
-
#
|
7
|
+
#
|
8
8
|
# It contains basic info about source as well as file content.
|
9
9
|
#
|
10
10
|
class SourceFile
|
@@ -29,7 +29,7 @@ module Jsus
|
|
29
29
|
|
30
30
|
#
|
31
31
|
# Initializes a SourceFile given the filename and options
|
32
|
-
#
|
32
|
+
#
|
33
33
|
# options:
|
34
34
|
# * <tt>:pool:</tt> -- an instance of Pool
|
35
35
|
# * <tt>:package:</tt> -- an instance of Package
|
@@ -38,9 +38,11 @@ module Jsus
|
|
38
38
|
#
|
39
39
|
def self.from_file(filename, options = {})
|
40
40
|
if File.exists?(filename)
|
41
|
-
source = File.
|
41
|
+
source = File.open(filename, 'r:utf-8') {|f| f.read }
|
42
|
+
bom = RUBY_VERSION =~ /1.9/ ? "\uFEFF" : "\xEF\xBB\xBF"
|
43
|
+
source.gsub!(bom, "")
|
42
44
|
yaml_data = source.match(%r(^/\*\s*(---.*?)\*/)m)
|
43
|
-
if (yaml_data && yaml_data[1] && header = YAML.load(yaml_data[1]))
|
45
|
+
if (yaml_data && yaml_data[1] && header = YAML.load(yaml_data[1]))
|
44
46
|
options[:header] = header
|
45
47
|
options[:relative_filename] = filename
|
46
48
|
options[:filename] = File.expand_path(filename)
|
@@ -78,7 +80,7 @@ module Jsus
|
|
78
80
|
header["description"]
|
79
81
|
end
|
80
82
|
|
81
|
-
#
|
83
|
+
#
|
82
84
|
# Returns an array of dependencies tags. Unordered.
|
83
85
|
#
|
84
86
|
def dependencies
|
@@ -95,7 +97,7 @@ module Jsus
|
|
95
97
|
#
|
96
98
|
def dependencies_names(options = {})
|
97
99
|
dependencies.map {|d| d.name(options) }
|
98
|
-
end
|
100
|
+
end
|
99
101
|
alias_method :requires_names, :dependencies_names
|
100
102
|
|
101
103
|
#
|
@@ -112,15 +114,15 @@ module Jsus
|
|
112
114
|
external_dependencies.map {|d| d.name }
|
113
115
|
end
|
114
116
|
|
115
|
-
#
|
117
|
+
#
|
116
118
|
# Returns an array with provides tags.
|
117
119
|
#
|
118
120
|
def provides
|
119
121
|
@provides
|
120
122
|
end
|
121
|
-
|
122
|
-
#
|
123
|
-
# Returns an array with provides names.
|
123
|
+
|
124
|
+
#
|
125
|
+
# Returns an array with provides names.
|
124
126
|
# Accepts options:
|
125
127
|
# * <tt>:short:</tt> -- whether provides should not prepend package name
|
126
128
|
# e.g. 'Class' instead of 'Core/Class' when in package 'Core')
|
@@ -134,13 +136,13 @@ module Jsus
|
|
134
136
|
def replaces
|
135
137
|
@replaces
|
136
138
|
end
|
137
|
-
|
139
|
+
|
138
140
|
|
139
141
|
#
|
140
142
|
# Returns a tag for source file, which this one is an extension for.
|
141
143
|
#
|
142
144
|
# E.g.: file Foo.js in package Core provides ['Class', 'Hash']. File Bar.js in package Bar
|
143
|
-
# extends 'Core/Class'. That means its contents would be appended to the Foo.js when compiling
|
145
|
+
# extends 'Core/Class'. That means its contents would be appended to the Foo.js when compiling
|
144
146
|
# the result.
|
145
147
|
#
|
146
148
|
def extends
|
@@ -162,7 +164,7 @@ module Jsus
|
|
162
164
|
@extensions = @extensions.flatten.compact.uniq
|
163
165
|
@extensions
|
164
166
|
end
|
165
|
-
|
167
|
+
|
166
168
|
def extensions=(new_value) # :nodoc:
|
167
169
|
@extensions = new_value
|
168
170
|
end
|
@@ -177,14 +179,14 @@ module Jsus
|
|
177
179
|
end
|
178
180
|
|
179
181
|
def include_extensions! # :nodoc:
|
180
|
-
if pool
|
182
|
+
if pool
|
181
183
|
provides.each do |p|
|
182
184
|
extensions << pool.lookup_extensions(p)
|
183
185
|
end
|
184
186
|
end
|
185
187
|
end
|
186
188
|
|
187
|
-
#
|
189
|
+
#
|
188
190
|
# Returns an array of files required by this files including all the filenames for extensions.
|
189
191
|
# SourceFile filename always goes first, all the extensions are unordered.
|
190
192
|
#
|
@@ -193,7 +195,7 @@ module Jsus
|
|
193
195
|
[filename, extensions.map {|e| e.filename}].flatten
|
194
196
|
end
|
195
197
|
|
196
|
-
#
|
198
|
+
#
|
197
199
|
# Returns a hash containing basic info with dependencies/provides tags' names
|
198
200
|
# and description for source file.
|
199
201
|
#
|
@@ -209,18 +211,26 @@ module Jsus
|
|
209
211
|
self.to_hash.inspect
|
210
212
|
end
|
211
213
|
# Private API
|
212
|
-
|
214
|
+
|
213
215
|
def header=(new_header) # :nodoc:
|
214
216
|
@header = new_header
|
215
217
|
# prepare defaults
|
216
218
|
@header["description"] ||= ""
|
217
219
|
# handle tags
|
218
|
-
@dependencies =
|
219
|
-
@
|
220
|
-
|
221
|
-
@
|
222
|
-
|
223
|
-
|
220
|
+
@dependencies = parse_tag_list(Array(@header["requires"]))
|
221
|
+
@provides = parse_tag_list(Array(@header["provides"]))
|
222
|
+
|
223
|
+
@extends = case @header["extends"]
|
224
|
+
when Array then Tag.new(@header["extends"][0])
|
225
|
+
when String then Tag.new(@header["extends"])
|
226
|
+
else nil
|
227
|
+
end
|
228
|
+
|
229
|
+
@replaces = case @header["replaces"]
|
230
|
+
when Array then Tag.new(@header["replaces"][0])
|
231
|
+
when String then Tag.new(@header["replaces"])
|
232
|
+
else nil
|
233
|
+
end
|
224
234
|
end
|
225
235
|
|
226
236
|
def content=(new_value) # :nodoc:
|
@@ -230,12 +240,30 @@ module Jsus
|
|
230
240
|
def content # :nodoc:
|
231
241
|
include_extensions
|
232
242
|
[@content, extensions.map {|e| e.content}].flatten.compact.join("\n")
|
233
|
-
end
|
234
|
-
|
243
|
+
end
|
244
|
+
|
235
245
|
def original_content # :nodoc:
|
236
246
|
@content
|
237
247
|
end
|
238
248
|
|
249
|
+
def parse_tag_list(tag_list)
|
250
|
+
tag_list.map do |tag_name|
|
251
|
+
case tag_name
|
252
|
+
when String
|
253
|
+
Tag.new(tag_name, :package => package)
|
254
|
+
when Hash
|
255
|
+
tags = []
|
256
|
+
tag_name.each do |pkg_name, sources|
|
257
|
+
normalized_package_name = pkg_name.sub(/(.+)\/.*$/, "\\1")
|
258
|
+
Array(sources).each do |source|
|
259
|
+
tags << Tag.new([normalized_package_name, source].join("/"))
|
260
|
+
end
|
261
|
+
end
|
262
|
+
tags
|
263
|
+
end
|
264
|
+
end.flatten
|
265
|
+
end # parse_tag_list
|
266
|
+
|
239
267
|
# Assigns an instance of Jsus::Pool to the source file.
|
240
268
|
# Also performs push to that pool.
|
241
269
|
def pool=(new_value)
|
@@ -247,17 +275,17 @@ module Jsus
|
|
247
275
|
def pool
|
248
276
|
@pool
|
249
277
|
end
|
250
|
-
|
278
|
+
|
251
279
|
def ==(other) # :nodoc:
|
252
280
|
eql?(other)
|
253
281
|
end
|
254
|
-
|
282
|
+
|
255
283
|
def eql?(other) # :nodoc:
|
256
284
|
other.kind_of?(SourceFile) && filename == other.filename
|
257
285
|
end
|
258
|
-
|
286
|
+
|
259
287
|
def hash
|
260
288
|
[self.class, filename].hash
|
261
289
|
end
|
262
290
|
end
|
263
|
-
end
|
291
|
+
end
|
data/lib/jsus/tag.rb
CHANGED
@@ -2,15 +2,15 @@ module Jsus
|
|
2
2
|
#
|
3
3
|
# Tag is basically just a string that contains a package name and a name for class
|
4
4
|
# (or not necessarily a class) which the given SourceFile provides/requires/extends/replaces.
|
5
|
-
#
|
5
|
+
#
|
6
6
|
class Tag
|
7
7
|
attr_accessor :package, :external # :nodoc:
|
8
8
|
|
9
9
|
# Constructors
|
10
|
-
|
10
|
+
|
11
11
|
#
|
12
12
|
# Creates a tag from given name/options.
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# The way it works may seem a bit tricky but actually it parses name/options
|
15
15
|
# combinations in different ways and may be best described by examples:
|
16
16
|
#
|
@@ -21,8 +21,8 @@ module Jsus
|
|
21
21
|
# d = Tag.new("Core/Class", :package => core) # :package_name => "Core", :name => "Class", :external => false
|
22
22
|
# mash = Package.new(...) # let's consider its name is 'Mash'
|
23
23
|
# e = Tag.new("Core/Class", :package => mash) # :package_name => "Core", :name => "Class", :external => true
|
24
|
-
#
|
25
|
-
# Between all those, tags b,c,d and e are equal, meaning they all use
|
24
|
+
#
|
25
|
+
# Between all those, tags b,c,d and e are equal, meaning they all use
|
26
26
|
# the same spot in Hash or whatever else.
|
27
27
|
#
|
28
28
|
def initialize(name, options = {})
|
@@ -60,7 +60,7 @@ module Jsus
|
|
60
60
|
# * +:short:+ -- whether the tag should try using short form
|
61
61
|
#
|
62
62
|
# Important note: only non-external tags support short forms.
|
63
|
-
#
|
63
|
+
#
|
64
64
|
# Tag.new('Core/Class').name(:short => true) # => 'Core/Class'
|
65
65
|
# core = Package.new(...) # let's consider its name is 'Core'
|
66
66
|
# Tag.new('Core/Class', :package => core).name(:short => true) # => 'Class'
|
@@ -91,9 +91,9 @@ module Jsus
|
|
91
91
|
super
|
92
92
|
end
|
93
93
|
end
|
94
|
-
|
94
|
+
|
95
95
|
# Private API
|
96
|
-
|
96
|
+
|
97
97
|
def self.normalize_name_and_options(name, options = {}) # :nodoc:
|
98
98
|
result = {}
|
99
99
|
name.gsub!(%r(^(\.)?/), "")
|
@@ -108,6 +108,7 @@ module Jsus
|
|
108
108
|
end
|
109
109
|
result[:name] = name
|
110
110
|
end
|
111
|
+
result[:package_name] = normalize_package_name(result[:package_name]) if result[:package_name]
|
111
112
|
result
|
112
113
|
end
|
113
114
|
|
@@ -118,7 +119,14 @@ module Jsus
|
|
118
119
|
def self.name_and_options_to_full_name(name, options = {}) # :nodoc:
|
119
120
|
normalized_options_to_full_name(normalize_name_and_options(name, options))
|
120
121
|
end
|
121
|
-
|
122
|
+
|
123
|
+
def self.normalize_package_name(name) # :nodoc:
|
124
|
+
package_chunks = name.split("/")
|
125
|
+
package_chunks.map do |pc|
|
126
|
+
Jsus::Util::Inflection.random_case_to_mixed_case(pc)
|
127
|
+
end.join("/")
|
128
|
+
end # normalize_name
|
129
|
+
|
122
130
|
def package_name=(new_value) # :nodoc:
|
123
131
|
@package_name = new_value
|
124
132
|
end
|
@@ -137,6 +145,6 @@ module Jsus
|
|
137
145
|
|
138
146
|
def inspect # :nodoc
|
139
147
|
"<Jsus::Tag: #{name}>"
|
140
|
-
end
|
148
|
+
end
|
141
149
|
end
|
142
150
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
module Jsus
|
3
|
+
module Util
|
4
|
+
#
|
5
|
+
# Simple file cache manager.
|
6
|
+
#
|
7
|
+
class FileCache
|
8
|
+
# Initializes filecache to given directory
|
9
|
+
def initialize(path)
|
10
|
+
@path = path
|
11
|
+
end # initialize
|
12
|
+
|
13
|
+
# Creates a file with given value for given key in cache directory
|
14
|
+
#
|
15
|
+
# Returns actual path for stored file.
|
16
|
+
def write(key, value)
|
17
|
+
item_path = generate_path(key)
|
18
|
+
FileUtils.mkdir_p(File.dirname(item_path))
|
19
|
+
File.open(item_path, 'w+') {|f| f.write(value) }
|
20
|
+
item_path
|
21
|
+
end # write
|
22
|
+
|
23
|
+
# If file exists for given cache key, returns path to that file.
|
24
|
+
# If file doesn't exist, returns nil.
|
25
|
+
def read(key)
|
26
|
+
item_path = generate_path(key)
|
27
|
+
File.exists?(item_path) ? item_path : nil
|
28
|
+
end # read
|
29
|
+
alias_method :exists?, :read
|
30
|
+
|
31
|
+
# If file with given key exists, returns path to it.
|
32
|
+
# Otherwise, writes value of yielded block.
|
33
|
+
def fetch(key, &block)
|
34
|
+
read(key) || write(key, yield)
|
35
|
+
end # fetch
|
36
|
+
|
37
|
+
# Deletes cache entry for given key.
|
38
|
+
def delete(key)
|
39
|
+
item_path = generate_path(key)
|
40
|
+
if File.exists?(item_path)
|
41
|
+
FileUtils.rm_f(item_path)
|
42
|
+
end
|
43
|
+
end # delete
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
# Generates path by cache key.
|
48
|
+
#
|
49
|
+
# Default strategy: append key to cache directory
|
50
|
+
# (slashes are replaced with dots)
|
51
|
+
def generate_path(key)
|
52
|
+
key = key.gsub(File::SEPARATOR, ".")
|
53
|
+
File.join(@path, key)
|
54
|
+
end # generate_path
|
55
|
+
end # class FileCache
|
56
|
+
end # module Util
|
57
|
+
end # module Jsus
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Jsus
|
2
|
+
module Util
|
3
|
+
# Various inflection helpers
|
4
|
+
module Inflection
|
5
|
+
class <<self
|
6
|
+
# Converts strings with various punctuation to pascal case
|
7
|
+
# hello_world => HelloWorld
|
8
|
+
# Oh.My.God => OhMyGod
|
9
|
+
# iAmCamelCase => IAmCamelCase
|
10
|
+
# some_Weird_._punctuation => SomeWeirdPunctuation
|
11
|
+
def random_case_to_mixed_case(string)
|
12
|
+
string.split(/[^a-zA-Z]+/).map {|chunk| capitalize(chunk) }.join
|
13
|
+
end # random_case_to_mixed_case
|
14
|
+
|
15
|
+
# Same as #random_case_to_mixed_case, but preserves dots
|
16
|
+
# color.fx => Color.Fx
|
17
|
+
def random_case_to_mixed_case_preserve_dots(string)
|
18
|
+
string.split(".").map {|c| random_case_to_mixed_case(c) }.join(".")
|
19
|
+
end # random_case_to_mixed_case
|
20
|
+
|
21
|
+
# Capitalizes first letter (doesn't do anything else to other letters, unlike String#capitalize)
|
22
|
+
def capitalize(string)
|
23
|
+
string[0,1].capitalize + string[1..-1].to_s
|
24
|
+
end # capitalize
|
25
|
+
|
26
|
+
# Downcases first letter
|
27
|
+
def decapitalize(string)
|
28
|
+
string[0,1].downcase + string[1..-1].to_s
|
29
|
+
end # decapitalize
|
30
|
+
|
31
|
+
# Translates MixedCase string to camel-case
|
32
|
+
def snake_case(string)
|
33
|
+
decapitalize(string.gsub(/(.)([A-Z])([a-z]+)/) {|_| "#{$1}_#{$2.downcase}#{$3}"}.
|
34
|
+
gsub(/[^A-Za-z_]+/, "_"))
|
35
|
+
end # snake_case
|
36
|
+
end # class <<self
|
37
|
+
end # module Inflection
|
38
|
+
end # module Util
|
39
|
+
end # module Jsus
|
data/lib/jsus/util.rb
CHANGED
data/lib/jsus.rb
CHANGED