wontomedia 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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