sproutcore 1.0.1009 → 1.0.1024
Sign up to get free protection for your applications and to get access to all the features.
- data/DISTRIBUTION.yml +14 -0
- data/Rakefile +150 -23
- data/VERSION.yml +4 -2
- data/frameworks/sproutcore/Buildfile +1 -1
- data/frameworks/sproutcore/frameworks/bootstrap/system/loader.js +1 -0
- data/frameworks/sproutcore/frameworks/datastore/models/record.js +66 -5
- data/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +14 -0
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record_attribute.js +28 -3
- data/frameworks/sproutcore/frameworks/desktop/english.lproj/alert.css +1 -1
- data/frameworks/sproutcore/frameworks/desktop/english.lproj/menu_item_view.css +22 -2
- data/frameworks/sproutcore/frameworks/desktop/english.lproj/panel.css +5 -1
- data/frameworks/sproutcore/frameworks/desktop/english.lproj/well.css +72 -0
- data/frameworks/sproutcore/frameworks/desktop/panes/alert.js +1 -1
- data/frameworks/sproutcore/frameworks/desktop/panes/picker.js +1 -1
- data/frameworks/sproutcore/frameworks/desktop/panes/select_button.js +33 -7
- data/frameworks/sproutcore/frameworks/desktop/system/root_responder.js +24 -23
- data/frameworks/sproutcore/frameworks/desktop/tests/views/list/ui_alternatingrows.js +130 -0
- data/frameworks/sproutcore/frameworks/desktop/tests/views/list/ui_row_heights.js +9 -10
- data/frameworks/sproutcore/frameworks/desktop/tests/views/list_item.js +4 -0
- data/frameworks/sproutcore/frameworks/desktop/tests/views/progress/ui.js +18 -9
- data/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/methods.js +7 -6
- data/frameworks/sproutcore/frameworks/desktop/tests/views/well/ui.js +54 -0
- data/frameworks/sproutcore/frameworks/desktop/views/button.js +21 -9
- data/frameworks/sproutcore/frameworks/desktop/views/checkbox.js +4 -3
- data/frameworks/sproutcore/frameworks/desktop/views/collection.js +1 -1
- data/frameworks/sproutcore/frameworks/desktop/views/grid.js +23 -14
- 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/radio.js +1 -2
- data/frameworks/sproutcore/frameworks/desktop/views/scroll.js +1 -1
- data/frameworks/sproutcore/frameworks/desktop/views/segmented.js +1 -1
- data/frameworks/sproutcore/frameworks/desktop/views/slider.js +1 -1
- data/frameworks/sproutcore/frameworks/desktop/views/source_list_group.js +1 -1
- data/frameworks/sproutcore/frameworks/desktop/views/well.js +80 -0
- data/frameworks/sproutcore/frameworks/foundation/fixtures/malformed.json +11 -0
- data/frameworks/sproutcore/frameworks/foundation/mixins/button.js +1 -1
- data/frameworks/sproutcore/frameworks/foundation/mixins/inline_text_field.js +5 -1
- data/frameworks/sproutcore/frameworks/foundation/panes/pane.js +1 -1
- data/frameworks/sproutcore/frameworks/foundation/system/cursor.js +11 -10
- data/frameworks/sproutcore/frameworks/foundation/system/event.js +16 -15
- data/frameworks/sproutcore/frameworks/foundation/system/render_context.js +3 -3
- data/frameworks/sproutcore/frameworks/foundation/system/request.js +6 -5
- data/frameworks/sproutcore/frameworks/foundation/system/response.js +26 -8
- data/frameworks/sproutcore/frameworks/foundation/system/root_responder.js +2 -2
- data/frameworks/sproutcore/frameworks/foundation/system/timer.js +2 -2
- data/frameworks/sproutcore/frameworks/foundation/system/utils.js +122 -13
- data/frameworks/sproutcore/frameworks/foundation/tests/system/core_query/jquery_core.js +2 -3
- data/frameworks/sproutcore/frameworks/foundation/tests/system/render_context/tag.js +9 -9
- data/frameworks/sproutcore/frameworks/foundation/tests/system/render_context/update.js +3 -3
- data/frameworks/sproutcore/frameworks/foundation/tests/system/request.js +27 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/system/utils/rect.js +99 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/views/image/ui.js +1 -1
- data/frameworks/sproutcore/frameworks/foundation/tests/views/view/updateLayer.js +1 -1
- data/frameworks/sproutcore/frameworks/foundation/views/image.js +3 -1
- data/frameworks/sproutcore/frameworks/foundation/views/label.js +1 -1
- data/frameworks/sproutcore/frameworks/runtime/system/cookie.js +160 -0
- data/frameworks/sproutcore/frameworks/runtime/system/object.js +1 -1
- data/frameworks/sproutcore/frameworks/runtime/tests/system/cookie.js +163 -0
- data/frameworks/sproutcore/themes/standard_theme/english.lproj/pane.css +12 -2
- data/lib/sproutcore/rack/proxy.rb +4 -2
- data/sproutcore-abbot.gemspec +23 -9
- metadata +32 -5
- data/frameworks/sproutcore/frameworks/desktop/english.lproj/images/standard_fade/000000.png +0 -0
- data/frameworks/sproutcore/frameworks/desktop/english.lproj/images/standard_fade/ffffff.png +0 -0
data/DISTRIBUTION.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# This file lists all subprojects that need to be installed in the directory
|
2
|
+
# for release. The Rakefile will track Git hash codes for each project in
|
3
|
+
# the VERSION.yml file
|
4
|
+
#
|
5
|
+
# Each entry should have the following format:
|
6
|
+
#
|
7
|
+
# path/to/item:
|
8
|
+
# repo: GIT/SVN URL
|
9
|
+
# scm: git|svn <- type of source control system. defaults to git
|
10
|
+
#
|
11
|
+
|
12
|
+
frameworks/sproutcore:
|
13
|
+
repo: git://github.com/sproutit/sproutcore.git
|
14
|
+
|
data/Rakefile
CHANGED
@@ -12,6 +12,13 @@ ROOT_PATH = File.dirname(__FILE__)
|
|
12
12
|
# files to ignore changes in
|
13
13
|
IGNORE_CHANGES = %w[.gitignore .gitmodules .DS_Store .gemspec VERSION.yml ^pkg ^tmp ^coverage]
|
14
14
|
|
15
|
+
# Get the DISTRIBUTION info
|
16
|
+
require 'yaml'
|
17
|
+
DIST = YAML.load File.read(File.expand_path(File.join(ROOT_PATH, 'DISTRIBUTION.yml')))
|
18
|
+
|
19
|
+
# Make empty to not use sudo
|
20
|
+
SUDO = 'sudo'
|
21
|
+
|
15
22
|
################################################
|
16
23
|
## LOAD DEPENDENCIES
|
17
24
|
##
|
@@ -19,6 +26,7 @@ begin
|
|
19
26
|
require 'rubygems'
|
20
27
|
require 'jeweler'
|
21
28
|
require 'extlib'
|
29
|
+
require 'fileutils'
|
22
30
|
require 'spec/rake/spectask'
|
23
31
|
|
24
32
|
$:.unshift(ROOT_PATH / 'lib')
|
@@ -26,6 +34,7 @@ begin
|
|
26
34
|
|
27
35
|
rescue LoadError => e
|
28
36
|
$stderr.puts "WARN: some required gems are not installed (try rake init to setup)"
|
37
|
+
$stderr.puts e
|
29
38
|
end
|
30
39
|
|
31
40
|
|
@@ -45,7 +54,11 @@ Jeweler::Tasks.new do |gemspec|
|
|
45
54
|
gemspec.add_dependency 'extlib', ">= 0.9.9"
|
46
55
|
gemspec.add_dependency 'erubis', ">= 2.6.2"
|
47
56
|
gemspec.add_dependency 'thor', '>= 0.11.7'
|
48
|
-
|
57
|
+
|
58
|
+
gemspec.add_development_dependency 'gemcutter', ">= 0.1.0"
|
59
|
+
gemspec.add_development_dependency 'jeweler', ">= 1.0.0"
|
60
|
+
gemspec.add_development_dependency 'rspec', ">= 1.2.0"
|
61
|
+
|
49
62
|
gemspec.rubyforge_project = "sproutcore"
|
50
63
|
gemspec.extra_rdoc_files.include *%w[History.txt README.txt]
|
51
64
|
|
@@ -59,17 +72,109 @@ Jeweler::RubyforgeTasks.new do |rubyforge|
|
|
59
72
|
rubyforge.doc_task = "rdoc"
|
60
73
|
end
|
61
74
|
|
75
|
+
Jeweler::GemcutterTasks.new
|
76
|
+
|
77
|
+
|
78
|
+
def git(path, cmd, log=true)
|
79
|
+
$stdout.puts("#{path.sub(ROOT_PATH, '')}: git #{cmd}") if log
|
80
|
+
git_path = path / '.git'
|
81
|
+
git_index = git_path / 'index'
|
82
|
+
|
83
|
+
# The env can become polluted; breaking git. This will avoid that.
|
84
|
+
%x[GIT_DIR=#{git_path}; GIT_WORK_TREE=#{path}; GIT_INDEX_FILE=#{git_index}; git #{cmd}]
|
85
|
+
end
|
86
|
+
|
62
87
|
################################################
|
63
88
|
## CORE TASKS
|
64
89
|
##
|
65
90
|
|
66
|
-
desc "performs an initial setup on the tools. Installs gems,
|
67
|
-
task :init
|
91
|
+
desc "performs an initial setup on the tools. Installs gems, checkout"
|
92
|
+
task :init => [:install_gems, 'dist:init', 'dist:update']
|
93
|
+
|
94
|
+
task :install_gems do
|
68
95
|
$stdout.puts "Installing gems (may ask for password)"
|
69
|
-
|
96
|
+
system %[#{SUDO} gem install rack json json_pure extlib erubis thor]
|
97
|
+
system %[#{SUDO} gem install jeweler gemcutter rspec] # dev req
|
98
|
+
end
|
99
|
+
|
100
|
+
namespace :dist do
|
101
|
+
|
102
|
+
desc "checkout any frameworks in the distribution"
|
103
|
+
task :init do
|
104
|
+
$stdout.puts "Setup distribution"
|
105
|
+
|
106
|
+
DIST.each do |rel_path, opts|
|
107
|
+
path = ROOT_PATH / rel_path
|
108
|
+
repo_url = opts['repo']
|
109
|
+
|
110
|
+
if !File.exists?(path / ".git")
|
111
|
+
$stdout.puts " Creating repo for #{rel_path}"
|
112
|
+
FileUtils.mkdir_p File.dirname(path)
|
113
|
+
|
114
|
+
$stdout.puts "\n> git clone #{repo_url} #{path}"
|
115
|
+
system %[git clone #{repo_url} #{path}]
|
116
|
+
|
117
|
+
else
|
118
|
+
$stdout.puts "Found #{rel_path}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
desc "make the version of each distribution item match the one in VERSION"
|
124
|
+
task :update => 'dist:init' do
|
125
|
+
$stdout.puts "Setup distribution"
|
126
|
+
|
127
|
+
# Use this to get the commit hash
|
128
|
+
version_file = ROOT_PATH / 'VERSION.yml'
|
129
|
+
if File.exist?(version_file)
|
130
|
+
versions = YAML.load File.read(version_file)
|
131
|
+
versions = (versions['dist'] || versions[:dist]) if versions
|
132
|
+
versions ||= {}
|
133
|
+
end
|
134
|
+
|
135
|
+
DIST.each do |rel_path, opts|
|
136
|
+
path = ROOT_PATH / rel_path
|
137
|
+
|
138
|
+
if File.exists?(path / ".git") && versions[rel_path]
|
139
|
+
sha = versions[rel_path]
|
140
|
+
|
141
|
+
$stdout.puts "\n> git fetch"
|
142
|
+
$stdout.puts git(path, 'fetch')
|
143
|
+
|
144
|
+
$stdout.puts "\n> git checkout #{sha}"
|
145
|
+
$stdout.puts git(path, 'checkout #{sha}')
|
146
|
+
else
|
147
|
+
$stdout.puts "WARN: cannot fix version for #{rel_path}"
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
namespace :release do
|
70
156
|
|
71
|
-
|
72
|
-
|
157
|
+
desc "tags the current repository and any distribution repositories. if you can push to distribution, then push tag as well"
|
158
|
+
task :tag => :update_version do
|
159
|
+
tag_name = "REL-#{RELEASE_VERSION}"
|
160
|
+
DIST.keys.push('abbot').each do |rel_path|
|
161
|
+
full_path = rel_path=='abbot' ? ROOT_PATH : (ROOT_PATH / rel_path)
|
162
|
+
git(full_path, "tag -f #{tag_name}")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
desc "prepare release. verify clean, update version, tag"
|
167
|
+
task :prepare => ['git:verify_clean', :update_version, :tag]
|
168
|
+
|
169
|
+
desc "release to rubyforge for old skool folks"
|
170
|
+
task :rubyforge => [:prepare, 'rubyforge:release']
|
171
|
+
|
172
|
+
desc "release to gemcutter for new skool kids"
|
173
|
+
task :gemcutter => [:prepare, 'gemcutter:release']
|
174
|
+
|
175
|
+
desc "one release to rule them all"
|
176
|
+
task :all => [:prepare, 'release:rubyforge', 'release:gemcutter']
|
177
|
+
|
73
178
|
end
|
74
179
|
|
75
180
|
desc "computes the current hash of the code. used to autodetect build changes"
|
@@ -107,7 +212,7 @@ task :hash_content do
|
|
107
212
|
paths.each do |path|
|
108
213
|
mtime = File.mtime(path)
|
109
214
|
mtime = mtime.nil? ? 0 : mtime.to_i
|
110
|
-
puts "detected file change: #{path.gsub(ROOT_PATH,'')}" if mtime > hash_date
|
215
|
+
$stdout.puts "detected file change: #{path.gsub(ROOT_PATH,'')}" if mtime > hash_date
|
111
216
|
cur_date = mtime if mtime > cur_date
|
112
217
|
end
|
113
218
|
|
@@ -127,7 +232,7 @@ task :hash_content do
|
|
127
232
|
|
128
233
|
# finally set the hash
|
129
234
|
CONTENT_HASH = hash_digest
|
130
|
-
puts "CONTENT_HASH = #{CONTENT_HASH}"
|
235
|
+
$stdout.puts "CONTENT_HASH = #{CONTENT_HASH}"
|
131
236
|
end
|
132
237
|
|
133
238
|
desc "updates the VERSION file, bumbing the build rev if the current commit has changed"
|
@@ -142,6 +247,7 @@ task :update_version => 'hash_content' do
|
|
142
247
|
minor = 0
|
143
248
|
build = 99
|
144
249
|
rev = '-0-'
|
250
|
+
dist = {}
|
145
251
|
|
146
252
|
if File.exist?(path)
|
147
253
|
yaml = YAML.load_file(path)
|
@@ -154,12 +260,35 @@ task :update_version => 'hash_content' do
|
|
154
260
|
build += 1 if rev != CONTENT_HASH #increment if needed
|
155
261
|
rev = CONTENT_HASH
|
156
262
|
|
157
|
-
|
263
|
+
# Update distribution versions
|
264
|
+
DIST.each do |rel_path, ignored|
|
265
|
+
dist_path = ROOT_PATH / rel_path
|
266
|
+
if File.exists?(dist_path)
|
267
|
+
dist_rev = git(dist_path, "log HEAD^..HEAD")
|
268
|
+
dist_rev = dist_rev.split("\n").first.scan(/commit ([^\s]+)/)
|
269
|
+
dist_rev = ((dist_rev || []).first || []).first
|
270
|
+
|
271
|
+
if dist_rev.nil?
|
272
|
+
$stdout.puts " WARN: cannot find revision for #{rel_path}"
|
273
|
+
else
|
274
|
+
dist[rel_path] = dist_rev
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
$stdout.puts "write version #{[major, minor, build].join('.')} => #{path}"
|
158
280
|
File.open(path, 'w+') do |f|
|
159
281
|
YAML.dump({
|
160
|
-
:major => major,
|
282
|
+
:major => major,
|
283
|
+
:minor => minor,
|
284
|
+
:patch => build,
|
285
|
+
:digest => rev,
|
286
|
+
:dist => dist
|
161
287
|
}, f)
|
162
288
|
end
|
289
|
+
|
290
|
+
RELEASE_VERSION = "#{major}.#{minor}.#{build}"
|
291
|
+
|
163
292
|
end
|
164
293
|
|
165
294
|
def fixup_gemspec
|
@@ -177,8 +306,6 @@ task :build => 'gemspec:generate' do
|
|
177
306
|
fixup_gemspec
|
178
307
|
end
|
179
308
|
|
180
|
-
#task "gemspec:generate" => 'git:verify_clean'
|
181
|
-
|
182
309
|
# Extend gemspec to rename afterware
|
183
310
|
task :gemspec do
|
184
311
|
fixup_gemspec
|
@@ -195,19 +322,19 @@ namespace :git do
|
|
195
322
|
|
196
323
|
desc "verifies there are no pending changes to commit to git"
|
197
324
|
task :verify_clean do
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
path = File.join(repo_name.split('/').unshift(ROOT_PATH))
|
203
|
-
end
|
325
|
+
DIST.keys.push('abbot').each do |repo_name|
|
326
|
+
full_path = repo_name=='abbot' ? ROOT_PATH : (ROOT_PATH / repo_name)
|
327
|
+
|
328
|
+
result = git(full_path, 'status')
|
204
329
|
|
205
|
-
result = `cd #{path}; git status`
|
206
330
|
if !(result =~ /nothing to commit \(working directory clean\)/)
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
331
|
+
if (repo_name != 'abbot') ||
|
332
|
+
(!(result =~ /#\n#\tmodified: VERSION.yml\n#\n/))
|
333
|
+
$stderr.puts "\nFATAL: Cannot complete task: changes are still pending in the '#{repo_name}' repository."
|
334
|
+
$stderr.puts " Commit your changes to git to continue.\n\n"
|
335
|
+
exit(1)
|
336
|
+
end
|
337
|
+
end
|
211
338
|
end
|
212
339
|
end
|
213
340
|
|
data/VERSION.yml
CHANGED
@@ -40,6 +40,7 @@ SC.setupBodyClassNames = function() {
|
|
40
40
|
if(borderRad) classNames.push('border-rad');
|
41
41
|
classNames.push(browser) ;
|
42
42
|
classNames.push(platform) ;
|
43
|
+
if (SC.browser.msie==7) classNames.push('ie7') ;
|
43
44
|
if (SC.browser.mobileSafari) classNames.push('mobile-safari') ;
|
44
45
|
el.className = classNames.join(' ') ;
|
45
46
|
} ;
|
@@ -76,7 +76,18 @@ SC.Record = SC.Object.extend(
|
|
76
76
|
}.property('storeKey').cacheable(),
|
77
77
|
|
78
78
|
/**
|
79
|
-
|
79
|
+
All records generally have a life cycle as they are created or loaded into
|
80
|
+
memory, modified, committed and finally destroyed. This life cycle is
|
81
|
+
managed by the status property on your record.
|
82
|
+
|
83
|
+
The status of a record is modelled as a finite state machine. Based on the
|
84
|
+
current state of the record, you can determine which operations are
|
85
|
+
currently allowed on the record and which are not.
|
86
|
+
|
87
|
+
In general, a record can be in one of five primary states; SC.Record.EMPTY,
|
88
|
+
SC.Record.BUSY, SC.Record.READY, SC.Record.DESTROYED, SC.Record.ERROR.
|
89
|
+
These are all described in more detail in the class mixin (below) where
|
90
|
+
they are defined.
|
80
91
|
|
81
92
|
@property {Number}
|
82
93
|
*/
|
@@ -293,6 +304,7 @@ SC.Record = SC.Object.extend(
|
|
293
304
|
storeKey = this.storeKey,
|
294
305
|
status = store.peekStatus(storeKey),
|
295
306
|
recordAttr = this[key],
|
307
|
+
recordType = SC.Store.recordTypeFor(storeKey),
|
296
308
|
attrs;
|
297
309
|
|
298
310
|
attrs = store.readEditableDataHash(storeKey);
|
@@ -310,10 +322,59 @@ SC.Record = SC.Object.extend(
|
|
310
322
|
SC.Store.idsByStoreKey[storeKey] = attrs[key] ;
|
311
323
|
this.propertyDidChange('id'); // Reset computed value
|
312
324
|
}
|
313
|
-
|
325
|
+
|
326
|
+
// if any aggregates, propagate the state
|
327
|
+
if(!recordType.aggregates || recordType.aggregates.length>0) {
|
328
|
+
this.propagateToAggregates();
|
329
|
+
}
|
330
|
+
|
314
331
|
return this ;
|
315
332
|
},
|
316
333
|
|
334
|
+
/**
|
335
|
+
This will also ensure that any aggregate records are also marked dirty
|
336
|
+
if this record changes.
|
337
|
+
|
338
|
+
Should not have to be called manually.
|
339
|
+
*/
|
340
|
+
propagateToAggregates: function() {
|
341
|
+
var storeKey = this.get('storeKey'),
|
342
|
+
recordType = SC.Store.recordTypeFor(storeKey),
|
343
|
+
idx, len, key, val, recs;
|
344
|
+
|
345
|
+
var aggregates = recordType.aggregates;
|
346
|
+
|
347
|
+
// if recordType aggregates are not set up yet, make sure to
|
348
|
+
// create the cache first
|
349
|
+
if(!aggregates) {
|
350
|
+
var dataHash = this.get('store').readDataHash(storeKey),
|
351
|
+
aggregates = [];
|
352
|
+
for(k in dataHash) {
|
353
|
+
if(this[k] && this[k].get && this[k].get('aggregate')===YES) {
|
354
|
+
aggregates.push(k);
|
355
|
+
}
|
356
|
+
}
|
357
|
+
recordType.aggregates = aggregates;
|
358
|
+
}
|
359
|
+
|
360
|
+
// now loop through all aggregate properties and mark their related
|
361
|
+
// record objects as dirty
|
362
|
+
for(idx=0,len=aggregates.length;idx<len;idx++) {
|
363
|
+
key = aggregates[idx];
|
364
|
+
val = this.get(key);
|
365
|
+
|
366
|
+
recs = SC.kindOf(val, SC.ManyArray) ? val : [val];
|
367
|
+
recs.forEach(function(rec) {
|
368
|
+
// write the dirty status
|
369
|
+
if(rec) {
|
370
|
+
rec.get('store').writeStatus(rec.get('storeKey'), this.get('status'));
|
371
|
+
rec.storeDidChangeProperties(YES);
|
372
|
+
}
|
373
|
+
}, this);
|
374
|
+
}
|
375
|
+
|
376
|
+
},
|
377
|
+
|
317
378
|
/**
|
318
379
|
Called by the store whenever the underlying data hash has changed. This
|
319
380
|
will notify any observers interested in data hash properties that they
|
@@ -380,7 +441,9 @@ SC.Record = SC.Object.extend(
|
|
380
441
|
|
381
442
|
if (!isRecord) {
|
382
443
|
attrValue = this.get(key);
|
383
|
-
if(attrValue!==undefined || attrValue
|
444
|
+
if(attrValue!==undefined || (attrValue===null && includeNull)) {
|
445
|
+
dataHash[key] = attrValue;
|
446
|
+
}
|
384
447
|
}
|
385
448
|
else if(isRecord) {
|
386
449
|
recHash = store.readDataHash(storeKey);
|
@@ -403,8 +466,6 @@ SC.Record = SC.Object.extend(
|
|
403
466
|
}
|
404
467
|
}
|
405
468
|
}
|
406
|
-
|
407
|
-
if (includeNull && dataHash[key]===undefined) dataHash[key] = null;
|
408
469
|
}
|
409
470
|
}
|
410
471
|
|
@@ -103,6 +103,20 @@ SC.RecordAttribute = SC.Object.extend(
|
|
103
103
|
*/
|
104
104
|
useIsoDate: YES,
|
105
105
|
|
106
|
+
/**
|
107
|
+
Can only be used for toOne or toMany relationship attributes. If YES,
|
108
|
+
this flag will ensure that any related objects will also be marked
|
109
|
+
dirty when this record dirtied.
|
110
|
+
|
111
|
+
Useful when you might have multiple related objects that you want to
|
112
|
+
consider in an 'aggregated' state. For instance, by changing a child
|
113
|
+
object (image) you might also want to automatically mark the parent
|
114
|
+
(album) dirty as well.
|
115
|
+
|
116
|
+
@property {Boolean}
|
117
|
+
*/
|
118
|
+
aggregate: NO,
|
119
|
+
|
106
120
|
// ..........................................................
|
107
121
|
// HELPER PROPERTIES
|
108
122
|
//
|
@@ -15,6 +15,9 @@ module("SC.RecordAttribute core methods", {
|
|
15
15
|
store: SC.Store.create()
|
16
16
|
});
|
17
17
|
|
18
|
+
// stick it to the window object so that objectForPropertyPath works
|
19
|
+
window.MyApp = MyApp;
|
20
|
+
|
18
21
|
MyApp.Foo = SC.Record.extend({
|
19
22
|
|
20
23
|
// test simple reading of a pass-through prop
|
@@ -57,7 +60,10 @@ module("SC.RecordAttribute core methods", {
|
|
57
60
|
|
58
61
|
});
|
59
62
|
|
60
|
-
MyApp.Bar = SC.Record.extend({
|
63
|
+
MyApp.Bar = SC.Record.extend({
|
64
|
+
parent: SC.Record.toOne('MyApp.Foo', { aggregate: YES }),
|
65
|
+
relatedMany: SC.Record.toMany('MyApp.Foo', { aggregate: YES })
|
66
|
+
});
|
61
67
|
|
62
68
|
SC.RunLoop.begin();
|
63
69
|
storeKeys = MyApp.store.loadRecords(MyApp.Foo, [
|
@@ -75,6 +81,7 @@ module("SC.RecordAttribute core methods", {
|
|
75
81
|
firstName: "Jane",
|
76
82
|
lastName: "Doe",
|
77
83
|
relatedTo: 'foo1',
|
84
|
+
relatedToAggregate: 'bar1',
|
78
85
|
anArray: 'notAnArray',
|
79
86
|
anObject: 'notAnObject',
|
80
87
|
nonIsoDate: "2009/06/10 8:55:50 +0000"
|
@@ -92,7 +99,7 @@ module("SC.RecordAttribute core methods", {
|
|
92
99
|
]);
|
93
100
|
|
94
101
|
MyApp.store.loadRecords(MyApp.Bar, [
|
95
|
-
{ guid: 'bar1', city: "Chicago" }
|
102
|
+
{ guid: 'bar1', city: "Chicago", parent: 'foo2', relatedMany: ['foo1', 'foo2'] }
|
96
103
|
]);
|
97
104
|
|
98
105
|
SC.RunLoop.end();
|
@@ -165,7 +172,6 @@ test("writing pass-through should simply set value", function() {
|
|
165
172
|
});
|
166
173
|
|
167
174
|
test("writing a value should override default value", function() {
|
168
|
-
|
169
175
|
equals(rec.get('defaultValue'), 'default', 'precond - returns default');
|
170
176
|
rec.set('defaultValue', 'not-default');
|
171
177
|
equals(rec.get('defaultValue'), 'not-default', 'newly written value should replace default value');
|
@@ -176,3 +182,22 @@ test("writing a date should generate an ISO date" ,function() {
|
|
176
182
|
equals(rec.set('date', date), rec, 'returns reciever');
|
177
183
|
equals(rec.readAttribute('date'), '2009-04-01T22:28:03-07:00', 'should have new time (%@)'.fmt(date.toString()));
|
178
184
|
});
|
185
|
+
|
186
|
+
test("writing an attribute should make relationship aggregate dirty" ,function() {
|
187
|
+
equals(bar.get('status'), SC.Record.READY_CLEAN, "precond - bar should be READY_CLEAN");
|
188
|
+
equals(rec2.get('status'), SC.Record.READY_CLEAN, "precond - rec2 should be READY_CLEAN");
|
189
|
+
|
190
|
+
bar.set('city', 'Oslo');
|
191
|
+
|
192
|
+
equals(rec2.get('status'), SC.Record.READY_DIRTY, "foo2 should be READY_DIRTY");
|
193
|
+
});
|
194
|
+
|
195
|
+
test("writing an attribute should make many relationship aggregate dirty" ,function() {
|
196
|
+
equals(bar.get('status'), SC.Record.READY_CLEAN, "precond - bar should be READY_CLEAN");
|
197
|
+
equals(rec2.get('status'), SC.Record.READY_CLEAN, "precond - rec2 should be READY_CLEAN");
|
198
|
+
|
199
|
+
bar.set('city', 'Oslo');
|
200
|
+
|
201
|
+
equals(rec.get('status'), SC.Record.READY_DIRTY, "foo1 should be READY_DIRTY");
|
202
|
+
equals(rec2.get('status'), SC.Record.READY_DIRTY, "foo2 should be READY_DIRTY");
|
203
|
+
});
|