wontomedia 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/Rakefile +9 -5
  2. data/VERSION.yml +2 -2
  3. data/app/controllers/admin_controller.rb +34 -11
  4. data/app/controllers/connections_controller.rb +8 -2
  5. data/app/controllers/items_controller.rb +213 -68
  6. data/app/helpers/format_helper.rb +22 -1
  7. data/app/helpers/items_helper.rb +43 -0
  8. data/app/models/connection.rb +128 -100
  9. data/app/models/item.rb +96 -0
  10. data/app/views/connections/_spo_select_controls.html.erb +65 -31
  11. data/app/views/connections/edit.html.erb +7 -2
  12. data/app/views/connections/index.html.erb +11 -4
  13. data/app/views/connections/show.html.erb +13 -5
  14. data/app/views/items/_class_select.html.erb +41 -0
  15. data/app/views/items/_content_examples.html.erb +21 -18
  16. data/app/views/items/_core_tasks.html.erb +40 -5
  17. data/app/views/items/_form_fields.html.erb +4 -4
  18. data/app/views/items/_inline_item_add_form.html.erb +48 -0
  19. data/app/views/items/_inline_scalar_add_form.html.erb +36 -0
  20. data/app/views/items/_most_populous_classes.html.erb +42 -0
  21. data/app/views/items/_type_select.html.erb +28 -26
  22. data/app/views/items/edit.html.erb +8 -2
  23. data/app/views/items/index.html.erb +5 -1
  24. data/app/views/items/new.html.erb +69 -54
  25. data/app/views/items/newpop.html.erb +18 -3
  26. data/app/views/items/show.html.erb +110 -7
  27. data/app/views/layouts/application.html.erb +2 -2
  28. data/app/views/layouts/base.html.erb +4 -2
  29. data/app/views/layouts/home.html.erb +2 -2
  30. data/assets/wontomedia-sample.rb +2 -0
  31. data/config/asset_packages.yml +1 -0
  32. data/config/cucumber.yml +11 -13
  33. data/db/fixtures/connections.yml +85 -4
  34. data/db/fixtures/items.yml +140 -8
  35. data/db/migrate/20100315135952_provide_scalar_objects.rb +32 -0
  36. data/db/migrate/20100321042343_add_timestamp_columns.rb +33 -0
  37. data/db/schema.rb +17 -11
  38. data/default-custom/app/views/items/home.html.erb +1 -1
  39. data/default-custom/public/stylesheets/wm.css +21 -4
  40. data/lib/helpers/connection_helper.rb +84 -1
  41. data/lib/helpers/item_helper.rb +16 -3
  42. data/lib/tasks/cucumber.rake +0 -2
  43. data/public/images/transparent_ltblue_background.png +0 -0
  44. data/public/images/{alert_background.png → transparent_white_background.png} +0 -0
  45. data/public/javascripts/forConnectionsForms.js +182 -41
  46. data/public/javascripts/forItemsForms.js +40 -5
  47. data/public/javascripts/forItemsShow.js +27 -0
  48. data/public/javascripts/itemCreatePopup.js +10 -3
  49. metadata +13 -5
data/Rakefile CHANGED
@@ -107,6 +107,9 @@ namespace :test do
107
107
  end
108
108
  Rake::Task['test:units'].comment =
109
109
  "Run the model tests in test/unit/app/models"
110
+
111
+ # but it's still a stupid naming convention:
112
+ task :models => :units
110
113
  end
111
114
 
112
115
 
@@ -172,11 +175,12 @@ namespace :test do
172
175
  ruby File.join( "policy", "ckFilesUtils", "ckCustomizationFilesPresent.rb" )
173
176
  end
174
177
 
175
- # alias
176
- task :integrations => :integration
178
+ # aliases
179
+ task :integrations => :integration # multiple tests -> plural task name
180
+ task :controllers => :functionals # Rails naming convention #fail
177
181
 
178
182
  desc "Execute all the tests for Ruby code."
179
- task :ruby_tests => [ "test:devs", "test:dbmigrations", "test:functionals",
183
+ task :ruby_tests => [ "test:devs", "test:db", "test:controllers",
180
184
  "test:integrations", "build", "cucumber:static_ok"]
181
185
  end # namespace :test
182
186
 
@@ -184,10 +188,10 @@ end # namespace :test
184
188
 
185
189
  # replace Rail's basic test task so that we get a reasonable execution order
186
190
  Rake::Task[:test].clear!
187
- desc 'Run all unit, functional, integration, and policy checks'
191
+ desc 'Run all unit, integration, and policy checks'
188
192
  task :tests => [ "test:policies", "asset:packager:build_all",
189
193
  # above two have side effects necessary for setup
190
- "test:devs", "test:dbmigrations", "test:functionals",
194
+ "test:devs", "test:db", "test:controllers",
191
195
  "test:javascripts", "test:integrations", "build",
192
196
  "cucumber:ok" ]
193
197
  # alias
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 1
4
- :patch: 1
3
+ :minor: 2
4
+ :patch: 0
@@ -108,31 +108,54 @@ class AdminController < ApplicationController
108
108
  flash[:error] =""
109
109
 
110
110
  params[:connection_upload][:connectionfile].readlines.each do |n3line|
111
- # this is a really, *really* bad N3 parser. Almost certainly won't
112
- # handly any but the most trivial input (like what we export :-)
113
- if n3line =~ /<#([^>]+)>[^<]+<#([^>]+)>[^<]+<#([^>]+)>[^.]+\./
111
+ err_str = nil;
112
+
113
+ # this is a really, *really* bad N3 parser. Almost certainly won't
114
+ # handly any but the most trivial input (like what we export :-)
115
+ # handle connections whose objects are scalar constants
116
+ if n3line =~ /<#([^>]+)>[^<]+<#([^>]+)>[^"]+"([^"]+)"[^.]+\./
117
+ e = Connection.new(
118
+ :subject => Item.find_by_name($1),
119
+ :predicate => Item.find_by_name($2),
120
+ :scalar_obj => $3,
121
+ :kind_of_obj => Connection::OBJECT_KIND_SCALAR,
122
+ :flags => 0
123
+ )
124
+ if e.nil?
125
+ err_str = "Couldn't create connection for #{$1} #{$2} '#{$3}'.\n"
126
+ else
127
+ if e.save
128
+ count += 1
129
+ else
130
+ err_str = "Couldn't save connection for #{$1} #{$2} '#{$3}'.\n"
131
+ end
132
+ end
133
+ # handle connections whose objects are Items
134
+ elsif n3line =~ /<#([^>]+)>[^<]+<#([^>]+)>[^<]+<#([^>]+)>[^.]+\./
114
135
  e = Connection.new(
115
- :subject => Item.find_by_name($1),
116
- :predicate => Item.find_by_name($2),
117
- :obj => Item.find_by_name($3),
118
- :flags => 0
136
+ :subject => Item.find_by_name($1),
137
+ :predicate => Item.find_by_name($2),
138
+ :obj => Item.find_by_name($3),
139
+ :kind_of_obj => Connection::OBJECT_KIND_ITEM,
140
+ :flags => 0
119
141
  )
120
142
  if e.nil?
121
143
  err_str = "Couldn't create connection for #{$1} #{$2} #{$3}.\n"
122
- logger.error(err_str)
123
- flash[:error] << err_str
124
144
  else
125
145
  if e.save
126
146
  count += 1
127
147
  else
128
148
  err_str = "Couldn't save connection for #{$1} #{$2} #{$3}.\n"
129
- logger.error(err_str)
130
- flash[:error] << err_str
131
149
  end
132
150
  end
133
151
  else
134
152
  unparsed += 1
135
153
  end
154
+
155
+ if (err_str)
156
+ logger.error(err_str)
157
+ flash[:error] << err_str
158
+ end
136
159
  end
137
160
 
138
161
  flash[:notice] = "Created #{count} new connections."
@@ -63,7 +63,11 @@ class ConnectionsController < ApplicationController
63
63
 
64
64
  if @connection.save
65
65
  flash[:notice] = 'Connection was successfully created.'
66
- redirect_to(@connection)
66
+ if params[:goto]
67
+ redirect_to('/' + params[:goto])
68
+ else
69
+ redirect_to(@connection)
70
+ end
67
71
  else
68
72
  populate_for_new_update
69
73
  @this_is_non_information_page = true
@@ -84,7 +88,9 @@ class ConnectionsController < ApplicationController
84
88
  # populate these here because I didn't want view causing db queries
85
89
  @subject = @connection.subject
86
90
  @predicate = @connection.predicate
87
- @obj = @connection.obj
91
+ if @connection.kind_of_obj == Connection::OBJECT_KIND_ITEM
92
+ @obj = @connection.obj
93
+ end
88
94
  @connection_desc = @connection.connection_desc
89
95
  end
90
96
 
@@ -26,6 +26,7 @@
26
26
  # starts. It can be deleted, this file reloaded, and then ItemHelper
27
27
  # references will work normally. Go figure. -- gei 2010/2/24
28
28
  require Rails.root.join( 'lib', 'helpers', 'item_helper')
29
+ require Rails.root.join( 'lib', 'helpers', 'connection_helper')
29
30
  require 'yaml'
30
31
 
31
32
  # See also the matching model Item
@@ -33,6 +34,7 @@ class ItemsController < ApplicationController
33
34
  # GET /
34
35
  def home
35
36
  @nouns = ItemHelper.nouns
37
+ @class_list = contributor_class_items
36
38
  render :layout => "home"
37
39
  end
38
40
 
@@ -67,8 +69,9 @@ class ItemsController < ApplicationController
67
69
 
68
70
  # GET /items/new
69
71
  def new
70
- @this_is_non_information_page = true
71
72
  @item = Item.new
73
+ setup_for_new
74
+ setup_for_form
72
75
  end
73
76
 
74
77
  # GET /items/new-pop
@@ -83,27 +86,31 @@ class ItemsController < ApplicationController
83
86
  # +views+; see their source for additional details.
84
87
  def newpop
85
88
  @item = Item.new
86
- @type = params[:type]
89
+ @popup_type = params[:popup_type]
90
+ setup_for_new
91
+ setup_for_form
87
92
  render :layout => "popup"
88
93
  end
89
94
 
90
95
  # POST /items
91
96
  def create
92
97
  type_string = params[:item][:sti_type]
98
+ if type_string.nil?
99
+ recover_from_create_failure(
100
+ 'Could not create. Item must have "Type" filled in.' )
101
+ end
93
102
  params[:item].delete :sti_type # don't mass-assign protected blah, blah
103
+
94
104
  @item = ItemHelper.new_typed_item(type_string, params[:item])
95
105
  @popup_flag = true if params[:popup_flag]
96
106
 
97
107
  if @item.nil?
98
- flash.now[:error] =
99
- 'Could not create. Item must have a type of either "Category" or "Individual".'
100
- @item = Item.new(params[:item]) # keep info already entered
101
- @item.sti_type = type_string
102
- @this_is_non_information_page = true
108
+ recover_from_create_failure 'Could not create.', type_string
103
109
  render :action => (@popup_flag ? "newpop" : "new" )
104
110
  elsif @item.name =~ /[:.]/ || !@item.save
105
- @item.errors.add :name, "cannot contain a period (.) or a colon (:)."
106
- @this_is_non_information_page = true
111
+ recover_from_create_failure '' do
112
+ @item.errors.add :name, "cannot contain a period (.) or a colon (:)."
113
+ end
107
114
  render :action => (@popup_flag ? "newpop" : "new" )
108
115
  else
109
116
  if @popup_flag
@@ -178,7 +185,7 @@ class ItemsController < ApplicationController
178
185
  # view can expect that all of the *predicate_id* values in the
179
186
  # Connections in this array will be different.
180
187
  # - The next array in @connection_list contains all of the
181
- # Connection objects whose *object_id* values are equal to
188
+ # Connection objects whose *obj_id* values are equal to
182
189
  # *@item*. Like all the other, this array won't be placed into
183
190
  # @connection_list if it would be empty. Also, in the event of
184
191
  # that a Connection references *@item* as both its subject and
@@ -243,20 +250,64 @@ class ItemsController < ApplicationController
243
250
  new_connection = Connection.new(
244
251
  :subject_id => connection.obj_id,
245
252
  :predicate_id => inverse_property_id,
246
- :obj_id => connection.subject_id
253
+ :obj_id => connection.subject_id,
254
+ :kind_of_obj => Connection::OBJECT_KIND_ITEM
247
255
  )
248
256
  used_as_subj << new_connection
249
257
  @inverses_map[new_connection] = connection
250
258
  end
251
259
  end
252
260
 
261
+ # add blank-object connections to serve as basis for connection "quick add"
262
+ @intances_of_type_item_classes = {}
263
+ if class_item = @item.instance_of
264
+ find_applied_properties( class_item ).each do |property_item|
265
+ if connection = Connection.first( :conditions =>
266
+ [ "subject_id = ? AND predicate_id = ?",
267
+ property_item.id, Item.find_by_name('has_scalar_object').id ])
268
+ new_kind = Connection::OBJECT_KIND_SCALAR
269
+ new_type_item = connection.obj
270
+
271
+ elsif Connection.first( :conditions =>
272
+ [ "subject_id = ? AND predicate_id = ?",
273
+ property_item.id, Item.find_by_name('has_item_object').id ])
274
+ new_kind = Connection::OBJECT_KIND_ITEM
275
+ new_type_item = connection = Connection.first( :conditions =>
276
+ [ "subject_id = ? AND predicate_id = ?",
277
+ property_item.id, Item.find_by_name('property_object_is').id ])
278
+
279
+ unless connection.nil?
280
+ new_type_item = connection.obj
281
+
282
+ unless @intances_of_type_item_classes[new_type_item]
283
+ @intances_of_type_item_classes[new_type_item] =
284
+ Connection.all( :conditions => [
285
+ "predicate_id = ? AND obj_id = ?",
286
+ Item.find_by_name('is_instance_of').id,
287
+ new_type_item.id ]).
288
+ map do |connection|
289
+ connection.subject
290
+ end
291
+ end
292
+ end
293
+ else
294
+ new_type_item = new_kind = nil
295
+ end
296
+
297
+ used_as_subj << Connection.new({ :subject_id => @item.id,
298
+ :predicate_id => property_item.id, :kind_of_obj => new_kind,
299
+ :type_item => new_type_item })
300
+ end
301
+ end
302
+
303
+
253
304
  # find all of the Items referenced by the connections the view will list
254
305
  @item_hash = {}
255
306
  [ used_as_subj, used_as_pred, used_as_obj ].each do |connection_array|
256
307
  connection_array.each do |connection|
257
308
  [ connection.subject, connection.predicate, connection.obj ].
258
309
  each do |item|
259
- unless @item_hash.has_key? item.id
310
+ unless item.nil? or @item_hash.has_key? item.id
260
311
  @item_hash[item.id] = item
261
312
  end
262
313
  end
@@ -271,67 +322,28 @@ class ItemsController < ApplicationController
271
322
  value_id = Item.find_by_name("value_relationship").id
272
323
  spo_id = Item.find_by_name("sub_property_of").id
273
324
 
274
- # first group, all connections *from* this item with value-type predicates
275
- connections = []
276
- used_as_subj.each do |connection|
277
- if TrippleNavigation.check_properties(
278
- :does => connection.predicate_id, :via => spo_id,
279
- :inherit_from => value_id )
280
- connections << connection
281
- end
282
- end
283
- used_as_subj -= connections # if done incrementally, breaks iterator
325
+ # first group, all connections *from* this item
326
+ # start with explicit connections
327
+ connections = used_as_subj
328
+ # sort and add as a group to array-of-arrays for view
284
329
  unless connections.empty?
285
- @connection_list << connections
286
- end
287
-
288
-
289
- # next N groups: 1 group for connections *from* this item with >1 of
290
- # same pred.
291
- #figure out which predicates occur >1, how many they occur, sort & group
292
- pred_counts = {}
293
- connection_using_pred = {}
294
- used_as_subj.each do |connection|
295
- if pred_counts[connection.predicate_id].nil?
296
- pred_counts[connection.predicate_id] = 1
297
- else
298
- pred_counts[connection.predicate_id] += 1
299
- end
300
- connection_using_pred[connection.predicate_id] = connection
301
- end
302
- subj_connections = pred_counts.keys
303
- subj_connections.sort! { |a,b| pred_counts[b] <=> pred_counts[a] }
304
- array_of_singles = []
305
- subj_connections.each do |predicate_id|
306
- if pred_counts[predicate_id] > 1
307
- connections = []
308
- used_as_subj.each do |connection|
309
- # lazy, more hashes would eliminate rescan
310
- if connection.predicate_id == predicate_id
311
- connections << connection
312
- end
313
- end
314
- unless connections.empty?
315
- @connection_list << connections
330
+ count_hash = {}
331
+ connections.each do |con|
332
+ if count_hash[con.predicate_id].nil?
333
+ count_hash[con.predicate_id] = 0
316
334
  end
317
- else
318
- array_of_singles << connection_using_pred[predicate_id]
335
+ count_hash[con.predicate_id] += 1
319
336
  end
320
- end
321
337
 
322
- # last group of connections *from* current item:
323
- # all connections w/ used-once pred's
324
- unless array_of_singles.empty?
325
- @connection_list << array_of_singles
338
+ @connection_list << ( connections.sort do |a,b|
339
+ ConnectionHelper.compare( a, b, count_hash )
340
+ end )
326
341
  end
327
342
 
328
343
 
344
+ # next groups
329
345
  used_as_obj.sort! do |a,b|
330
- if a.predicate_id == b.predicate_id
331
- a.obj_id <=> b.obj_id
332
- else
333
- a.predicate_id <=> b.predicate_id
334
- end
346
+ a.predicate_id <=> b.predicate_id
335
347
  end
336
348
  unless used_as_obj.empty?
337
349
  @connection_list << used_as_obj
@@ -357,7 +369,7 @@ class ItemsController < ApplicationController
357
369
  return
358
370
  end
359
371
 
360
- @this_is_non_information_page = true
372
+ setup_for_form
361
373
  end
362
374
 
363
375
  # PUT /items/1
@@ -384,7 +396,7 @@ class ItemsController < ApplicationController
384
396
  params[:item][:name] =~ /[:.]/ ) ||
385
397
  !@item.update_attributes(params[:item])
386
398
  @item.errors.add :name, "cannot contain a period (.) or a colon (:)."
387
- @this_is_non_information_page = true
399
+ setup_for_form
388
400
  render :action => "edit"
389
401
  else
390
402
  flash[:notice] = 'Item was successfully updated.'
@@ -404,7 +416,19 @@ class ItemsController < ApplicationController
404
416
  if (@item.flags & Item::DATA_IS_UNALTERABLE) != 0
405
417
  flash[:error] = 'This Item cannot be altered.'
406
418
  redirect_to item_by_name_path(@item.name)
407
- elsif !(Connection.all( :conditions =>
419
+ return
420
+ end
421
+
422
+ class_item = @item.class_item
423
+ if class_item
424
+ Connection.first( :conditions =>
425
+ [ "subject_id = ? AND predicate_id = ? AND obj_id = ?",
426
+ @item.id,
427
+ Item.find_by_name('is_instance_of'),
428
+ class_item.id ] ).destroy
429
+ end
430
+
431
+ if !(Connection.all( :conditions =>
408
432
  [ "subject_id = ? OR predicate_id = ? OR obj_id = ?",
409
433
  @item.id, @item.id, @item.id ]).empty?)
410
434
  flash[:error] = 'This Item is in use by 1+ Connections. ' +
@@ -455,4 +479,125 @@ class ItemsController < ApplicationController
455
479
 
456
480
  render :text => ("<id>" + id.to_s + "</id>\n")
457
481
  end
482
+
483
+ private
484
+
485
+ def all_class_items
486
+ # Get the special property items "sub_class_of" and
487
+ # "is_instance_of"; find all of the connections that use them as
488
+ # predicates; find all of the items that are those connections'
489
+ # subjects ("s_c_o") or objects (both). At some point we might
490
+ # want to allow sub-properties of the two special ones to also
491
+ # imply that items represent classes.
492
+
493
+ class_list = []
494
+ [ 'sub_class_of', 'is_instance_of' ].each do |property_name|
495
+ Connection.all( :conditions => [ "predicate_id = ?",
496
+ Item.find_by_name(property_name).id ]).each do |connection|
497
+
498
+ # look at objects of both special properties (assuming con. valid)
499
+ if connection.kind_of_obj == Connection::OBJECT_KIND_ITEM
500
+ class_list << connection.obj
501
+ end
502
+
503
+ #and at subjects of just
504
+ if property_name == 'sub_class_of'
505
+ class_list << connection.subject
506
+ end
507
+ end
508
+ end
509
+
510
+ class_list.uniq
511
+ end
512
+
513
+ def contributor_class_items
514
+ list = all_class_items.select do |class_item|
515
+ (class_item.flags & Item::DATA_IS_UNALTERABLE) == 0
516
+ end
517
+
518
+ iio_item_id = Item.find_by_name('is_instance_of').id
519
+ counts = {}
520
+ list.each do |class_item|
521
+ counts[class_item] = Connection.all( :conditions => [
522
+ "predicate_id = ? AND obj_id = ?", iio_item_id, class_item.id ] ).length
523
+ end
524
+
525
+ list.sort{|a,b| counts[b] <=> counts[a]}
526
+ end
527
+
528
+ def map_of_item_types_for_class_items( class_items )
529
+ citi_item_id = Item.find_by_name('class_item_type_is').id
530
+ class_to_item_map = {}
531
+ class_items.each do |class_item|
532
+ connection = Connection.first( :conditions => [
533
+ "subject_id = ? AND predicate_id = ?",
534
+ class_item.id, citi_item_id ] )
535
+
536
+ unless connection.nil?
537
+ class_to_item_map[class_item] =
538
+ ItemHelper.sti_type_for_ItemType( connection.obj.name )
539
+ end
540
+ end
541
+
542
+ class_to_item_map
543
+ end
544
+
545
+ def setup_for_form
546
+ @class_list = all_class_items
547
+ @class_to_item_map = map_of_item_types_for_class_items @class_list
548
+ @this_is_non_information_page = true
549
+ end
550
+
551
+ def setup_for_new
552
+ if params[:class_item]
553
+ id = params[:class_item]
554
+ class_item = Item.find_by_id(id)
555
+ if class_item
556
+ @item.class_item_id = id
557
+
558
+ if params[:sti_type].nil? # probably no class_item_type_is to find
559
+ citi_item_id = Item.find_by_name('class_item_type_is').id
560
+ connection = Connection.first( :conditions => [
561
+ "subject_id = ? AND predicate_id = ?",
562
+ class_item.id, citi_item_id ] )
563
+ unless connection.nil?
564
+ @item.sti_type =
565
+ ItemHelper.sti_type_for_ItemType( connection.obj.name )
566
+ end
567
+ end
568
+ end
569
+ end
570
+
571
+ if params[:sti_type]
572
+ @item.sti_type = params[:sti_type]
573
+ end
574
+ end
575
+
576
+ def recover_from_create_failure( message_string, type_string = nil )
577
+ flash.now[:error] = message_string
578
+ @item = Item.new(params[:item]) # keep info already entered
579
+ @item.sti_type = type_string unless type_string.nil?
580
+ yield if block_given?
581
+ setup_for_form
582
+ end
583
+
584
+ def find_applied_properties( class_item )
585
+ recursive_find_applied_properties(
586
+ class_item, Item.find_by_name('applies_to_class') ).flatten
587
+ end
588
+ def recursive_find_applied_properties( class_item, joining_prop )
589
+ con_array = Connection.all( :conditions => [
590
+ "predicate_id = ? AND obj_id = ?",
591
+ joining_prop.id, class_item.id ] )
592
+ return [] if con_array.nil?
593
+ con_array.map do |connection|
594
+ item = connection.subject
595
+ if item.sti_type == ItemHelper::ITEM_PROPERTY_CLASS_NAME
596
+ item
597
+ else # prop is_instance_of prop-class; prop-class applied_to_class 'us'
598
+ recursive_find_applied_properties( item,
599
+ Item.find_by_name('is_instance_of') )
600
+ end
601
+ end
602
+ end
458
603
  end