sproutcore 1.0.1009 → 1.0.1024
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/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
|
+
});
|