sproutit-sproutcore 1.0.20090721145281 → 1.0.20090721145282
Sign up to get free protection for your applications and to get access to all the features.
- data/Buildfile +4 -3
- data/VERSION.yml +2 -2
- data/buildtasks/entry.rake +3 -0
- data/buildtasks/manifest.rake +35 -9
- data/buildtasks/target.rake +25 -6
- data/frameworks/sproutcore/Buildfile +10 -0
- data/frameworks/sproutcore/frameworks/datastore/data_sources/data_source.js +41 -20
- data/frameworks/sproutcore/frameworks/datastore/data_sources/fixtures.js +14 -43
- data/frameworks/sproutcore/frameworks/datastore/models/record.js +11 -0
- data/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +6 -3
- data/frameworks/sproutcore/frameworks/datastore/system/nested_store.js +5 -1
- data/frameworks/sproutcore/frameworks/datastore/system/query.js +10 -7
- data/frameworks/sproutcore/frameworks/datastore/system/record_array.js +19 -20
- data/frameworks/sproutcore/frameworks/datastore/system/store.js +126 -93
- data/frameworks/sproutcore/frameworks/datastore/tests/data_sources/fixtures.js +9 -3
- data/frameworks/sproutcore/frameworks/datastore/tests/models/many_attribute.js +6 -1
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record/core_methods.js +28 -3
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record/destroy.js +13 -5
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record/storeDidChangeProperties.js +46 -23
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record/writeAttribute.js +29 -5
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record_attribute.js +13 -4
- data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/chain.js +109 -0
- data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/commitChanges.js +69 -15
- data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/commitChangesFromNestedStore.js +20 -1
- data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/dataHashDidChange.js +4 -1
- data/frameworks/sproutcore/frameworks/datastore/tests/system/query/find_all.js +56 -6
- data/frameworks/sproutcore/frameworks/datastore/tests/system/store/commitRecord.js +9 -2
- data/frameworks/sproutcore/frameworks/datastore/tests/system/store/core_methods.js +45 -2
- data/frameworks/sproutcore/frameworks/datastore/tests/system/store/recordDidChange.js +0 -1
- data/frameworks/sproutcore/frameworks/datastore/tests/system/store/retrieveRecord.js +53 -6
- data/frameworks/sproutcore/frameworks/datastore/tests/system/store/writeDataHash.js +0 -5
- data/frameworks/sproutcore/frameworks/desktop/panes/menu.js +47 -27
- data/frameworks/sproutcore/frameworks/desktop/system/drag.js +5 -4
- data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/mouse.js +23 -12
- data/frameworks/sproutcore/frameworks/desktop/tests/views/list/render.js +92 -0
- data/frameworks/sproutcore/frameworks/desktop/tests/views/select_field/methods.js +104 -53
- data/frameworks/sproutcore/frameworks/desktop/tests/views/select_field/ui.js +2 -0
- data/frameworks/sproutcore/frameworks/desktop/views/button.js +4 -3
- data/frameworks/sproutcore/frameworks/desktop/views/collection.js +6 -2
- data/frameworks/sproutcore/frameworks/desktop/views/list.js +9 -0
- data/frameworks/sproutcore/frameworks/desktop/views/list_item.js +2 -2
- data/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +3 -3
- data/frameworks/sproutcore/frameworks/desktop/views/popup_button.js +9 -1
- data/frameworks/sproutcore/frameworks/desktop/views/select_field.js +80 -102
- data/frameworks/sproutcore/frameworks/foundation/controllers/array.js +0 -1
- data/frameworks/sproutcore/frameworks/foundation/english.lproj/text_field.css +5 -1
- data/frameworks/sproutcore/frameworks/foundation/panes/pane.js +8 -1
- data/frameworks/sproutcore/frameworks/foundation/private/tree_item_observer.js +0 -1
- data/frameworks/sproutcore/frameworks/foundation/system/datetime.js +31 -3
- data/frameworks/sproutcore/frameworks/foundation/system/event.js +0 -4
- data/frameworks/sproutcore/frameworks/foundation/system/render_context.js +3 -7
- data/frameworks/sproutcore/frameworks/foundation/system/request.js +3 -4
- data/frameworks/sproutcore/frameworks/foundation/system/user_defaults.js +78 -17
- data/frameworks/sproutcore/frameworks/foundation/system/utils.js +9 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/controllers/array/single_case.js +2 -2
- data/frameworks/sproutcore/frameworks/foundation/tests/system/core_query/jquery_selector.js +2 -2
- data/frameworks/sproutcore/frameworks/foundation/tests/system/datetime.js +5 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/system/request.js +2 -2
- data/frameworks/sproutcore/frameworks/foundation/tests/system/user_defaults.js +1 -4
- data/frameworks/sproutcore/frameworks/foundation/tests/validators/validator.js +20 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/views/image/ui.js +13 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/ui.js +132 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/views/view/isVisibleInWindow.js +6 -3
- data/frameworks/sproutcore/frameworks/foundation/validators/validator.js +8 -5
- data/frameworks/sproutcore/frameworks/foundation/views/image.js +18 -5
- data/frameworks/sproutcore/frameworks/foundation/views/text_field.js +292 -21
- data/frameworks/sproutcore/frameworks/foundation/views/view.js +13 -14
- data/frameworks/sproutcore/frameworks/mini/license.js +28 -0
- data/frameworks/sproutcore/frameworks/runtime/core.js +35 -0
- data/frameworks/sproutcore/frameworks/runtime/mixins/enumerable.js +1 -1
- data/frameworks/sproutcore/frameworks/runtime/system/sparse_array.js +79 -5
- data/frameworks/sproutcore/frameworks/runtime/tests/mixins/enumerable.js +6 -6
- data/frameworks/sproutcore/frameworks/runtime/tests/system/sparse_array.js +53 -0
- data/frameworks/sproutcore/frameworks/testing/system/plan.js +4 -0
- data/frameworks/sproutcore/frameworks/testing/system/runner.js +1 -1
- data/frameworks/sproutcore/themes/standard_theme/english.lproj/radio.css +4 -0
- data/gen/design/Buildfile +23 -0
- data/gen/design/README +1 -0
- data/gen/design/USAGE +10 -0
- data/gen/design/templates/english.lproj/@filename@.js +16 -0
- data/gen/page/Buildfile +36 -0
- data/gen/page/README +1 -0
- data/gen/page/USAGE +15 -0
- data/gen/page/templates/pages/@target_name@/Buildfile +16 -0
- data/gen/page/templates/pages/@target_name@/core.js +22 -0
- data/gen/page/templates/pages/@target_name@/english.lproj/body.css +1 -0
- data/gen/page/templates/pages/@target_name@/english.lproj/body.rhtml +7 -0
- data/gen/page/templates/pages/@target_name@/english.lproj/strings.js +15 -0
- data/gen/view/README +1 -1
- data/gen/view/USAGE +5 -5
- data/lib/sproutcore/builders/base.rb +13 -2
- data/lib/sproutcore/builders/html.rb +28 -1
- data/lib/sproutcore/builders/minify.rb +84 -18
- data/lib/sproutcore/builders/test.rb +2 -1
- data/lib/sproutcore/helpers/entry_sorter.rb +16 -1
- data/lib/sproutcore/helpers/static_helper.rb +32 -4
- data/lib/sproutcore/helpers/tag_helper.rb +65 -0
- data/lib/sproutcore/models/manifest.rb +40 -6
- data/lib/sproutcore/models/target.rb +12 -3
- data/lib/sproutcore/rack/builder.rb +56 -4
- data/lib/sproutcore/tools/manifest.rb +1 -0
- data/lib/sproutcore/tools/server.rb +1 -0
- data/lib/sproutcore/tools.rb +21 -1
- data/lib/sproutcore.rb +13 -0
- metadata +16 -1
@@ -130,7 +130,14 @@ module SC
|
|
130
130
|
# self
|
131
131
|
#
|
132
132
|
def compile(render_engine, input_path, content_for_key = nil)
|
133
|
-
|
133
|
+
|
134
|
+
if content_for_key.nil?
|
135
|
+
if @in_partial
|
136
|
+
content_for_key = :_partial_
|
137
|
+
else
|
138
|
+
content_for_key = self.default_content_for_key
|
139
|
+
end
|
140
|
+
end
|
134
141
|
|
135
142
|
if !File.exist?(input_path)
|
136
143
|
raise "html_builder could compile file at #{input_path} because the file could not be found"
|
@@ -140,6 +147,7 @@ module SC
|
|
140
147
|
@renderer = render_engine # save for capture...
|
141
148
|
|
142
149
|
input = File.read(input_path)
|
150
|
+
|
143
151
|
content_for content_for_key do
|
144
152
|
_render_compiled_template( render_engine.compile(input) )
|
145
153
|
end
|
@@ -150,6 +158,25 @@ module SC
|
|
150
158
|
|
151
159
|
private
|
152
160
|
|
161
|
+
# Renders an entry as a partial. This will insert the results inline
|
162
|
+
# instead of into a content div.
|
163
|
+
def render_partial(entry)
|
164
|
+
|
165
|
+
# save off
|
166
|
+
old_partial = @content_for__partial_
|
167
|
+
old_in_partial = @in_partial
|
168
|
+
|
169
|
+
@content_for__partial_ = ''
|
170
|
+
@in_partial = true
|
171
|
+
render_entry(entry)
|
172
|
+
ret = @content_for__partial_
|
173
|
+
@content_for__partial_ = old_partial
|
174
|
+
@in_partial = old_in_partial
|
175
|
+
|
176
|
+
return ret
|
177
|
+
|
178
|
+
end
|
179
|
+
|
153
180
|
# Renders a single entry. The entry will be staged and then its
|
154
181
|
# render task will be executed.
|
155
182
|
def render_entry(entry)
|
@@ -33,35 +33,101 @@ module SC
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def build_css(dst_path)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
yui_root = File.expand_path(File.join(LIBPATH, '..', 'vendor', 'yui-compressor'))
|
37
|
+
jar_path = File.join(yui_root, 'yuicompressor-2.4.2.jar')
|
38
|
+
FileUtils.mkdir_p(File.dirname(dst_path)) # make sure loc exists...
|
39
|
+
filecompress = "java -jar " + jar_path + " --charset utf-8 --line-break 0 --nomunge --preserve-semi --disable-optimizations " + entry.source_path + " -o \"" + dst_path + "\" 2>&1"
|
40
|
+
SC.logger.info 'Compressing CSS with YUI .... '+ dst_path
|
41
|
+
SC.logger.debug `#{filecompress}`
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
if $?.exitstatus != 0
|
44
|
+
_report_error(output, entry.filename, entry.source_path)
|
45
|
+
SC.logger.fatal("!!!!YUI compressor failed, please check that your css code is valid.")
|
46
|
+
SC.logger.fatal("!!!!Failed compressing CSS... "+ dst_path)
|
47
|
+
end
|
48
|
+
end
|
48
49
|
|
49
50
|
# Minify some javascript by invoking the YUI compressor.
|
50
51
|
def build_javascript(dst_path)
|
51
52
|
yui_root = File.expand_path(File.join(LIBPATH, '..', 'vendor', 'yui-compressor'))
|
52
53
|
jar_path = File.join(yui_root, 'yuicompressor-2.4.2.jar')
|
53
54
|
FileUtils.mkdir_p(File.dirname(dst_path)) # make sure loc exists...
|
54
|
-
filecompress = "java -jar " + jar_path + " --charset utf-8 --line-break 80 " + entry.source_path + " -o " + dst_path
|
55
|
-
SC.logger.info 'Compressing with YUI
|
56
|
-
|
57
|
-
|
55
|
+
filecompress = "java -jar " + jar_path + " --charset utf-8 --line-break 80 " + entry.source_path + " -o \"" + dst_path + "\" 2>&1"
|
56
|
+
SC.logger.info 'Compressing with YUI: '+ dst_path + "..."
|
57
|
+
|
58
|
+
output = `#{filecompress}` # It'd be nice to just read STDERR, but
|
59
|
+
# I can't find a reasonable, commonly-
|
60
|
+
# installed, works-on-all-OSes solution.
|
58
61
|
if $?.exitstatus != 0
|
59
|
-
|
62
|
+
_report_error(output, entry.filename, entry.source_path)
|
63
|
+
SC.logger.fatal("!!!!YUI compressor failed, please check that your js code is valid")
|
60
64
|
SC.logger.fatal("!!!!Failed compressing ... "+ dst_path)
|
61
65
|
end
|
62
66
|
|
63
67
|
end
|
64
|
-
|
65
|
-
end
|
66
68
|
|
69
|
+
|
70
|
+
def _report_error(output, input_filename, input_filepath)
|
71
|
+
# The output might have some clues to what exactly was wrong, and it'll
|
72
|
+
# be convenient for users if we include the subset. So we'll read the
|
73
|
+
# line numbers from any output lines that start with "[ERROR]" those
|
74
|
+
# lines, too.
|
75
|
+
if output
|
76
|
+
parsed_a_line = false
|
77
|
+
output.each_line { |output_line|
|
78
|
+
output_line = output_line.chomp
|
79
|
+
if (output_line =~ /^\[ERROR\] (\d+):(\d+):.*/) != nil
|
80
|
+
line_number = $1
|
81
|
+
position = $2
|
82
|
+
parsed_a_line = true
|
83
|
+
if ( position && position.to_i > 0 )
|
84
|
+
# Read just that line and output it.
|
85
|
+
# (sed -n '3{;p;q;}' would probably be faster, but not
|
86
|
+
# universally available)
|
87
|
+
line_number = line_number.to_i
|
88
|
+
line_counter = 1
|
89
|
+
begin
|
90
|
+
file = File.new(input_filepath, "r")
|
91
|
+
outputted_line = false
|
92
|
+
previous_line = nil
|
93
|
+
while (file_line = file.gets)
|
94
|
+
|
95
|
+
if ( line_counter == line_number )
|
96
|
+
message = "YUI compressor error: #{output_line} on line #{line_number} of #{input_filename}:\n"
|
97
|
+
if !previous_line.nil?
|
98
|
+
message += " [#{line_number - 1}] #{previous_line}"
|
99
|
+
end
|
100
|
+
message += " --> [#{line_number}] #{file_line}"
|
101
|
+
|
102
|
+
SC.logger.error message
|
103
|
+
outputted_line = true
|
104
|
+
break
|
105
|
+
else
|
106
|
+
previous_line = file_line
|
107
|
+
end
|
108
|
+
line_counter += 1
|
109
|
+
end
|
110
|
+
file.close
|
111
|
+
rescue => err
|
112
|
+
SC.logger.error "Could not read the actual line from the file: #{err}"
|
113
|
+
end
|
114
|
+
|
115
|
+
if !outputted_line
|
116
|
+
SC.logger.error "YUI compressor error: #{output_line}, but couldn't read that line in the input file"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
}
|
121
|
+
|
122
|
+
# If we didn't handle at least one line of output specially, then
|
123
|
+
# just output it all.
|
124
|
+
if !parsed_a_line
|
125
|
+
SC.logger.error output
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
end
|
132
|
+
|
67
133
|
end
|
@@ -48,7 +48,8 @@ module SC
|
|
48
48
|
# conflict with any others.
|
49
49
|
def render_jstest(entry)
|
50
50
|
lines = readlines(entry.staging_path)
|
51
|
-
|
51
|
+
pathname = entry.staging_path.gsub(/^.+\/staging\//,'').gsub(/"/, '\"')
|
52
|
+
lines.unshift %[<script type="text/javascript">\nif (typeof SC !== "undefined") {\n SC.mode = "TEST_MODE";\n SC.filename = "#{pathname}"; \n}\n(function() {\n]
|
52
53
|
lines.push %[\n})();\n</script>\n]
|
53
54
|
@content_for_final = (@content_for_final || '') + lines.join("")
|
54
55
|
end
|
@@ -29,7 +29,22 @@ module SC
|
|
29
29
|
def sort(entries)
|
30
30
|
# first sort entries by filename - ignoring case
|
31
31
|
entries = entries.sort do |a,b|
|
32
|
-
|
32
|
+
a = (a.filename || '').to_s.downcase
|
33
|
+
b = (b.filename || '').to_s.downcase
|
34
|
+
|
35
|
+
# lproj/foo_page.js and main.js are loaded last
|
36
|
+
a_kind = (a =~ /lproj\/.+_page\.js$/) ? 1 : -1
|
37
|
+
a_kind = 2 if a =~ /main.js$/
|
38
|
+
|
39
|
+
b_kind = (b =~ /lproj\/.+_page\.js$/) ? 1 : -1
|
40
|
+
b_kind = 2 if b =~ /main.js$/
|
41
|
+
|
42
|
+
if a_kind != b_kind
|
43
|
+
a_kind <=> b_kind
|
44
|
+
else
|
45
|
+
a <=> b
|
46
|
+
end
|
47
|
+
|
33
48
|
end
|
34
49
|
all_entries = entries.dup # needed for sort...
|
35
50
|
|
@@ -94,7 +94,7 @@ module SC
|
|
94
94
|
|
95
95
|
# include either the entry URL or URL of ordered entries
|
96
96
|
# depending on setup
|
97
|
-
if combine_javascript
|
97
|
+
if cur_target.config.combine_javascript
|
98
98
|
urls << cur_entry.cacheable_url
|
99
99
|
else
|
100
100
|
urls += cur_entry.ordered_entries.map { |e| e.cacheable_url }
|
@@ -115,9 +115,27 @@ module SC
|
|
115
115
|
urls.join("\n")
|
116
116
|
end
|
117
117
|
|
118
|
+
# Attempts to render the named entry as a partial
|
119
|
+
#
|
120
|
+
# === Options
|
121
|
+
# language:: the language to use. defaults to current
|
122
|
+
#
|
123
|
+
def partial(resource_name, opts = {})
|
124
|
+
resource_name = resource_name.to_s
|
125
|
+
m = self.manifest
|
126
|
+
if opts[:language]
|
127
|
+
m = target.manifest_for(:language => opts[:language]).build!
|
128
|
+
end
|
129
|
+
|
130
|
+
entry = m.find_entry(resource_name, :hidden => true, :entry_type => :html)
|
131
|
+
return entry.nil? ? '' : render_partial(entry)
|
132
|
+
end
|
133
|
+
|
118
134
|
# Returns the URL for the named resource
|
119
135
|
def sc_static(resource_name, opts = {})
|
120
136
|
|
137
|
+
resource_name = resource_name.to_s
|
138
|
+
|
121
139
|
# determine which manifest to search. if a language is explicitly
|
122
140
|
# specified, lookup manifest for that language. otherwise use
|
123
141
|
# current manifest.
|
@@ -127,10 +145,19 @@ module SC
|
|
127
145
|
end
|
128
146
|
|
129
147
|
entry = m.find_entry(resource_name)
|
130
|
-
|
148
|
+
return '' if entry.nil?
|
149
|
+
return entry.friendly_url if opts[:friendly] && entry.friendly_url
|
150
|
+
return entry.cacheable_url
|
131
151
|
end
|
132
152
|
alias_method :static_url, :sc_static
|
133
153
|
|
154
|
+
# Returns the URL of the named target's index.html, if it has one
|
155
|
+
def sc_target(resource_name, opts = {})
|
156
|
+
opts[:friendly] = true
|
157
|
+
resource_name = "#{resource_name}:index.html"
|
158
|
+
sc_static(resource_name, opts)
|
159
|
+
end
|
160
|
+
|
134
161
|
# Allows you to specify HTML resource this html template should be
|
135
162
|
# merged into. Optionally also specify the layout file to use when
|
136
163
|
# building this resource.
|
@@ -168,8 +195,9 @@ module SC
|
|
168
195
|
return ret
|
169
196
|
end
|
170
197
|
|
171
|
-
def title
|
172
|
-
|
198
|
+
def title(cur_target=nil)
|
199
|
+
cur_target = self.target if cur_target.nil?
|
200
|
+
cur_target.config.title || cur_target.target_name.to_s.sub(/^\//,'').gsub(/[-_\/]/,' ').split(' ').map { |x| x.capitalize }.join(' ')
|
173
201
|
end
|
174
202
|
|
175
203
|
private
|
@@ -84,6 +84,71 @@ module SC
|
|
84
84
|
fix_double_escape(html_escape(html.to_s))
|
85
85
|
end
|
86
86
|
|
87
|
+
# Simple link_to can wrap a passed string with a link to a specified
|
88
|
+
# target or static asset. If you pass a block then the block will be
|
89
|
+
# invoked and its resulting content linked. You can also pass
|
90
|
+
# :popup, :title, :id, :class, and :style
|
91
|
+
def link_to(content, opts=nil, &block)
|
92
|
+
if block_given?
|
93
|
+
concat(link_to(capture(&block), content), block.binding);
|
94
|
+
return ''
|
95
|
+
end
|
96
|
+
|
97
|
+
if !content.instance_of?(String) && opts.nil?
|
98
|
+
opts = content
|
99
|
+
content = nil
|
100
|
+
end
|
101
|
+
|
102
|
+
opts = { :href => opts } if opts.instance_of? String
|
103
|
+
opts = HashStruct.new(opts)
|
104
|
+
html_attrs = HashStruct.new
|
105
|
+
is_target = false
|
106
|
+
|
107
|
+
if opts.href
|
108
|
+
html_attrs.href = opts.href
|
109
|
+
elsif opts.target
|
110
|
+
|
111
|
+
is_target = (target.target_name.to_sym == "/#{opts.target.to_s}".to_sym)
|
112
|
+
|
113
|
+
# supply title if needed
|
114
|
+
if content.nil?
|
115
|
+
cur_target = is_target ? target : target.target_for(opts.target)
|
116
|
+
content = title(cur_target) if cur_target
|
117
|
+
content = opts.target if content.nil?
|
118
|
+
end
|
119
|
+
|
120
|
+
# if current==false, then don't link if current target matches
|
121
|
+
if !opts.current.nil? && (opts.current==false) && is_target
|
122
|
+
return %(<span class="anchor current">#{content}</span>)
|
123
|
+
end
|
124
|
+
|
125
|
+
html_attrs.href = sc_target(opts.target, :language => opts.language)
|
126
|
+
elsif opts.static
|
127
|
+
html_attrs.href = sc_static(opts.static, :language => opts.language)
|
128
|
+
end
|
129
|
+
|
130
|
+
if opts.popup
|
131
|
+
popup = opts.popup
|
132
|
+
html_attrs.target = popup.instance_of?(String) ? popup : '_blank'
|
133
|
+
end
|
134
|
+
|
135
|
+
%w[title id class style].each do |key|
|
136
|
+
html_attrs[key] = opts[key] if opts[key]
|
137
|
+
end
|
138
|
+
|
139
|
+
# add "current" class name
|
140
|
+
if is_target
|
141
|
+
html_attrs[:class] = [html_attrs[:class], 'current'].compact.join(' ')
|
142
|
+
end
|
143
|
+
|
144
|
+
ret = ["<a "]
|
145
|
+
html_attrs.each { |k,v| ret << [k,'=','"',v,'" '].join('') }
|
146
|
+
ret << '>'
|
147
|
+
ret << content
|
148
|
+
ret << '</a>'
|
149
|
+
return ret.join('')
|
150
|
+
end
|
151
|
+
|
87
152
|
private
|
88
153
|
def content_tag_string(name, content, options)
|
89
154
|
tag_options = options ? tag_options(options) : ""
|
@@ -88,6 +88,8 @@ module SC
|
|
88
88
|
return self
|
89
89
|
end
|
90
90
|
|
91
|
+
def built?; @is_built; end
|
92
|
+
|
91
93
|
# Resets the manifest entries. this is called before a build is
|
92
94
|
# performed. This will reset only the entries, none of the other props.
|
93
95
|
#
|
@@ -213,6 +215,10 @@ module SC
|
|
213
215
|
end
|
214
216
|
opts.staging_path ||= unique_staging_path(staging_path)
|
215
217
|
|
218
|
+
# generate a unique cache path from the staging page. just sub the
|
219
|
+
# staging root for the cache root
|
220
|
+
opts.cache_path ||= unique_cache_path(entry.cache_path)
|
221
|
+
|
216
222
|
# copy other useful entries
|
217
223
|
opts.source_entry = entry
|
218
224
|
opts.source_entries = [entry]
|
@@ -289,11 +295,28 @@ module SC
|
|
289
295
|
#
|
290
296
|
def find_entry(fragment, opts = {}, seen=nil)
|
291
297
|
|
298
|
+
entry_extname = entry_rootname = ret = target_name = nil
|
299
|
+
|
300
|
+
# optionally you can specify an explicit target name
|
301
|
+
split_index = fragment.index(':') # find first index
|
302
|
+
unless split_index.nil?
|
303
|
+
target_name = '/' + fragment[0..(split_index-1)] if split_index>0
|
304
|
+
fragment = fragment[(split_index+1)..-1] # remove colon
|
305
|
+
end
|
306
|
+
|
307
|
+
# find the current manifest
|
308
|
+
if target_name
|
309
|
+
cur_target = self.target.target_for(target_name) || self.target
|
310
|
+
cur_manifest = cur_target.manifest_for(self.variation).build!
|
311
|
+
else
|
312
|
+
cur_manifest = self
|
313
|
+
end
|
314
|
+
|
292
315
|
extname = File.extname(fragment)
|
293
316
|
rootname = fragment.sub(/#{extname}$/, '')
|
294
|
-
entry_extname = entry_rootname = nil
|
295
317
|
|
296
|
-
|
318
|
+
# look on our own target only if target is named
|
319
|
+
ret = cur_manifest.entries(:hidden => opts[:hidden]).reject do |entry|
|
297
320
|
if entry.has_options?(opts)
|
298
321
|
entry_extname = File.extname(entry.filename)
|
299
322
|
entry_rootname = entry.filename.sub(/#{entry_extname}$/,'')
|
@@ -301,16 +324,17 @@ module SC
|
|
301
324
|
else
|
302
325
|
ext_match = false
|
303
326
|
end
|
327
|
+
|
304
328
|
!(ext_match && (/#{rootname}$/ =~ entry_rootname))
|
305
329
|
end
|
306
|
-
|
330
|
+
|
307
331
|
ret = ret.first
|
308
|
-
|
332
|
+
|
309
333
|
# if no match was found, search the same manifests in required targets
|
310
334
|
if ret.nil?
|
311
335
|
seen = Set.new if seen.nil?
|
312
|
-
seen <<
|
313
|
-
|
336
|
+
seen << cur_manifest.target
|
337
|
+
cur_manifest.target.expand_required_targets.each do |t|
|
314
338
|
next if seen.include?(t) # avoid recursion
|
315
339
|
|
316
340
|
manifest = t.manifest_for(self.variation).build!
|
@@ -330,6 +354,16 @@ module SC
|
|
330
354
|
end
|
331
355
|
return path
|
332
356
|
end
|
357
|
+
|
358
|
+
# Finds a unique cache path starting with the root proposed staging
|
359
|
+
# path.
|
360
|
+
def unique_cache_path(path)
|
361
|
+
paths = entries(:hidden => true).map { |e| e.cache_path }
|
362
|
+
while paths.include?(path)
|
363
|
+
path = path.sub(/(__\$[0-9]+)?(\.\w+)?$/,"__$#{next_staging_uuid}\\2")
|
364
|
+
end
|
365
|
+
return path
|
366
|
+
end
|
333
367
|
|
334
368
|
protected
|
335
369
|
|
@@ -241,7 +241,10 @@ module SC
|
|
241
241
|
# === Returns
|
242
242
|
# A build number string
|
243
243
|
#
|
244
|
-
def compute_build_number(seen=nil)
|
244
|
+
def compute_build_number(seen=nil, opts = {})
|
245
|
+
|
246
|
+
# reset cache if forced to recompute
|
247
|
+
build_number = nil if opts[:force]
|
245
248
|
|
246
249
|
# Look for a global build_numbers hash and try that
|
247
250
|
if (build_numbers = config.build_numbers)
|
@@ -272,10 +275,16 @@ module SC
|
|
272
275
|
# causing infinite loops. Normally this should not be necessary, but
|
273
276
|
# we put this here to gaurd against misconfigured projects
|
274
277
|
seen ||= []
|
275
|
-
|
278
|
+
|
279
|
+
_targets = required_targets(:theme => true).sort do |a,b|
|
280
|
+
(a.target_name||'').to_s <=> (b.target_name||'').to_s
|
281
|
+
end
|
282
|
+
|
283
|
+
_targets.each do |ct|
|
276
284
|
next if seen.include?(ct)
|
285
|
+
ct.prepare!
|
277
286
|
seen << ct
|
278
|
-
digests <<
|
287
|
+
digests << ct.compute_build_number(seen, :force => true)
|
279
288
|
end
|
280
289
|
|
281
290
|
# Finally digest the complete string - tada! build number
|
@@ -171,18 +171,70 @@ module SC
|
|
171
171
|
# Reloads the project if reloading is enabled. At maximum this will
|
172
172
|
# reload the project every 5 seconds.
|
173
173
|
def reload_project!
|
174
|
+
|
175
|
+
monitor_project!
|
176
|
+
|
174
177
|
# don't reload if no project or is disabled
|
175
178
|
return if @project.nil? || !@project.config.reload_project
|
176
179
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
if reload_delay > 5
|
180
|
+
if @project_did_change
|
181
|
+
@project_did_change = false
|
181
182
|
SC.logger.info "Rebuilding project manifest"
|
182
183
|
@project.reload!
|
183
184
|
end
|
184
185
|
end
|
186
|
+
|
187
|
+
def monitor_project!
|
188
|
+
if !@should_monitor
|
189
|
+
@should_monitor = true
|
190
|
+
@project_root = @project.project_root
|
191
|
+
|
192
|
+
# collect initial info on project
|
193
|
+
files = Dir.glob(@project_root / '**' / '*')
|
194
|
+
# follow 1-level of symlinks
|
195
|
+
files += Dir.glob(@project_root / '**' / '*' / '**' / '*')
|
196
|
+
tmp_path = /^#{Regexp.escape(@project_root / 'tmp')}/
|
197
|
+
files.reject! { |f| f =~ tmp_path }
|
198
|
+
files.reject! { |f| File.directory?(f) }
|
199
|
+
|
200
|
+
@project_file_count = files.size
|
201
|
+
@project_mtime = files.map { |x| File.mtime(x).to_i }.max
|
202
|
+
|
203
|
+
Thread.new do
|
204
|
+
while @should_monitor
|
205
|
+
|
206
|
+
# only need to start scanning again 2 seconds after the last
|
207
|
+
# request was serviced.
|
208
|
+
reload_delay = (Time.now - @last_reload_time)
|
209
|
+
if reload_delay > 2
|
210
|
+
files = Dir.glob(@project_root / '**' / '*')
|
211
|
+
# follow 1-level of symlinks
|
212
|
+
files += Dir.glob(@project_root / '**' / '*' / '**' / '*')
|
213
|
+
tmp_path = /^#{Regexp.escape(@project_root / 'tmp')}/
|
214
|
+
files.reject! { |f| f =~ tmp_path }
|
215
|
+
files.reject! { |f| File.directory?(f) }
|
216
|
+
|
217
|
+
cur_file_count = files.size
|
218
|
+
cur_mtime = files.map { |x| File.mtime(x).to_i }.max
|
219
|
+
|
220
|
+
if (@project_file_count != cur_file_count) || (@project_mtime != cur_mtime)
|
221
|
+
SC.logger.info "Detected project change. Will rebuild manifest"
|
222
|
+
@project_did_change = true
|
223
|
+
@project_file_count = cur_file_count
|
224
|
+
@project_mtime = cur_mtime
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
sleep(5)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
185
233
|
|
234
|
+
def stop_monitor!
|
235
|
+
@should_monitor = false
|
236
|
+
end
|
237
|
+
|
186
238
|
def target_for(url)
|
187
239
|
|
188
240
|
# get targets
|
data/lib/sproutcore/tools.rb
CHANGED
@@ -253,15 +253,35 @@ module SC
|
|
253
253
|
if (languages = options.languages).nil?
|
254
254
|
languages = targets.map { |t| t.installed_languages }
|
255
255
|
else
|
256
|
-
languages = languages.split('
|
256
|
+
languages = languages.split(',').map { |l| l.to_sym }
|
257
257
|
end
|
258
258
|
languages.flatten.uniq.compact
|
259
259
|
end
|
260
260
|
|
261
|
+
# Discovers build numbers requested for the build and sets them in the
|
262
|
+
# in the env if needed.
|
263
|
+
def find_build_numbers(*targets)
|
264
|
+
if options['build-numbers']
|
265
|
+
numbers = {}
|
266
|
+
options['build-numbers'].split(',').each do |pair|
|
267
|
+
pair = pair.split(':')
|
268
|
+
if pair.length < 2
|
269
|
+
fatal! "Could not parse build numbers! #{options['build-numbers']}"
|
270
|
+
end
|
271
|
+
numbers["/#{pair[0]}"] = pair[1]
|
272
|
+
end
|
273
|
+
SC.env.build_numbers = numbers
|
274
|
+
SC.logger.info "Using build numbers: #{numbers.map { |k,v| "#{k}: #{v}" }.join(',')}"
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
261
278
|
# Core method to process command line options and then build a manifest.
|
262
279
|
# Shared by sc-manifest, sc-build and sc-docs commands.
|
263
280
|
def build_manifests(*targets)
|
264
281
|
|
282
|
+
# setup build numbers
|
283
|
+
find_build_numbers(*targets)
|
284
|
+
|
265
285
|
requires_project! # get project
|
266
286
|
targets = find_targets(*targets) # get targets
|
267
287
|
languages = find_languages(*targets) # get languages
|
data/lib/sproutcore.rb
CHANGED
@@ -125,6 +125,19 @@ module SproutCore
|
|
125
125
|
def self.project; @project; end
|
126
126
|
def self.project=(project); @project = project; end
|
127
127
|
|
128
|
+
# Attempts to load a project for the current working directory or from the
|
129
|
+
# passed directory location. Returns nil if no project could be detected.
|
130
|
+
# This is just a shorthand for creating a Project object. It is useful
|
131
|
+
# when using the build tools as a Ruby library
|
132
|
+
def self.load_project(path = nil, opts = {})
|
133
|
+
path = File.expand_path(path.nil? ? Dir.pwd : path)
|
134
|
+
if FalseClass === opts[:discover]
|
135
|
+
SC::Project.load path, :parent => SC.builtin_project
|
136
|
+
else # attempt to autodiscover unless disabled
|
137
|
+
SC::Project.load_nearest_project path, :parent => SC.builtin_project
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
128
141
|
end # module SC
|
129
142
|
|
130
143
|
SC = SproutCore # alias
|