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
@@ -153,12 +153,17 @@ module FormatHelper
153
153
  end
154
154
 
155
155
  def logo_image
156
+ result = ''
157
+ if WontoMedia.site_logo_title
158
+ result += "<div width='99%'>#{WontoMedia.site_logo_title}</div>\n"
159
+ end
156
160
  logo_name = (File.exists?(
157
161
  Rails.root.join( 'public', 'images', 'logo.jpg'))) ?
158
162
  '/images/logo.jpg' :
159
163
  '/images/logo.png'
160
- image_tag( logo_name, :alt=>'Logo', :width=>'99%',
164
+ result += image_tag( logo_name, :alt=>'Logo', :width=>'99%',
161
165
  :style=>'margin-bottom: 0.7ex;' )
166
+ result
162
167
  end
163
168
 
164
169
 
@@ -178,4 +183,20 @@ module FormatHelper
178
183
  page_name +
179
184
  WontoMedia.site_content_url_postfix
180
185
  end
186
+
187
+ def ruby_hash_to_javascript( js_name, ruby_hash, prefix = 'id' )
188
+ has_started = false
189
+ js = "<script type='text/javascript'>\n"
190
+ js += " #{js_name} = {\n"
191
+
192
+ ruby_hash.keys.each do |class_item|
193
+ js += ",\n" if has_started
194
+ has_started = true
195
+ js += " #{prefix}#{class_item.id}: '#{ruby_hash[class_item]}'"
196
+ end
197
+
198
+ js += "\n"
199
+ js += " };\n"
200
+ js + "</script>"
201
+ end
181
202
  end
@@ -102,4 +102,47 @@ module ItemsHelper
102
102
  end
103
103
  end
104
104
  end
105
+
106
+ # produce an array of all the system's class items suitable for the
107
+ # "options" argument to select_tag, sorted as follows:
108
+ # * first: no-selection option
109
+ # * 2nd: +@item+'s currently-selected class, if any
110
+ # * non-built-in classes, sorted alphabetically
111
+ # * built-in classes
112
+ def class_options_list_for(item)
113
+ option_array = [ [ "- class of item -", "" ] ]
114
+
115
+ this_class_item = item.class_item
116
+ unless this_class_item.nil?
117
+ option_array.concat [[ h("#{this_class_item.name}"), this_class_item.id ]]
118
+ end
119
+
120
+ groups = @class_list.group_by do |item|
121
+ if this_class_item && item.id == this_class_item.id
122
+ :discard
123
+ elsif (item.flags & Item::DATA_IS_UNALTERABLE) == 0
124
+ :contributor
125
+ else
126
+ :builtin
127
+ end
128
+ end
129
+
130
+ if groups[:contributor]
131
+ option_array.concat(
132
+ groups[:contributor].sort{ |a,b|
133
+ a.name.upcase <=> b.name.upcase }.map{ |item|
134
+ [ h("#{item.name}"), item.id ] } )
135
+ option_array.concat [ [ " ----", "invalid" ] ]
136
+ end
137
+
138
+ if groups[:builtin]
139
+ option_array.concat(
140
+ groups[:builtin].sort{ |a,b|
141
+ a.name <=> b.name }.map{ |item|
142
+ [ h("#{item.name}"), item.id ] } )
143
+ end
144
+
145
+ options_for_select( option_array,
146
+ this_class_item.nil? ? "" : this_class_item.id )
147
+ end
105
148
  end
@@ -36,16 +36,26 @@ class Connection < ActiveRecord::Base
36
36
  # This constant is a bit mask for Item.flags. A non-zero value
37
37
  # indicates that the Item instance should not be user-modifiable.
38
38
  DATA_IS_UNALTERABLE = 1
39
+ OBJECT_KIND_ITEM = 'item'
40
+ OBJECT_KIND_SCALAR = 'scalar'
41
+
42
+
43
+ # convenience attribute allows item representing a connection's object's
44
+ # type (if specified by a relationship somewhere in the database) to
45
+ # be attached to the connection once it has been looked up once
46
+ attr_accessor :type_item
39
47
 
40
48
 
41
49
  before_validation :complex_validations
42
- # validates_presence_of :subject, :predicate, :obj
50
+ # validates_presence_of :subject, :predicate
43
51
  #explicitly do the equivalent of the above in complex_validations because
44
52
  #c_v has to be a "before" callback (so it's return value is checked), which
45
53
  #means other validations aren't run yet, so can't count on these IDs to
46
54
  #be present/valid. Ugh.
55
+
56
+ # validates_inclusion_of :kind_of_obj, :in => %w(item scalar)
47
57
  # validates_uniqueness_of :subject_id, :scope => [:predicate_id, :obj_id]
48
- #this works, but is subsumed by checks in c_v, so eliminated duplication
58
+ #these work, but are subsumed by checks in c_v, so eliminated duplication
49
59
 
50
60
  belongs_to :subject, :class_name => "Item"
51
61
  belongs_to :predicate, :class_name => "Item"
@@ -74,134 +84,152 @@ private
74
84
  # checks fail, this method returns +false+ and places the
75
85
  # appropriate message(s) in the +errors+ flash.
76
86
  def complex_validations() #:doc:
77
- [ [subject_id, :subject], [predicate_id, :predicate],
78
- [obj_id, :obj] ].each do |tocheck|
79
- field, symbol = *tocheck
80
- if field.nil?
81
- errors.add symbol, "can't be blank."
82
- return false
87
+ if kind_of_obj == OBJECT_KIND_ITEM
88
+ [ [subject_id, :subject], [predicate_id, :predicate],
89
+ [obj_id, :obj] ].each do |tocheck|
90
+ field, symbol = *tocheck
91
+ if field.nil?
92
+ errors.add symbol, "can't be blank."
93
+ return false
94
+ end
83
95
  end
84
- end
85
96
 
86
- # is there an existing connection with a predicate that is a
87
- # sub-property or super-property of the current predicate?
88
- # check for duplicate connection and all super-properties
89
- TrippleNavigation.relation_and_all_superproperties(
90
- predicate_id) do |super_prop|
91
- unless (Connection.all( :conditions => [
92
- "subject_id = ? AND predicate_id = ? AND obj_id = ?",
93
- subject_id, super_prop, obj_id ] ).empty? )
94
- errors.add :subject, 'relationship (or equivalent) already exists.'
95
- return false
97
+ # is there an existing connection with a predicate that is a
98
+ # sub-property or super-property of the current predicate?
99
+ # check for duplicate connection and all super-properties
100
+ TrippleNavigation.relation_and_all_superproperties(
101
+ predicate_id) do |super_prop|
102
+ unless (Connection.all( :conditions => [
103
+ "subject_id = ? AND predicate_id = ? AND obj_id = ?",
104
+ subject_id, super_prop, obj_id ] ).empty? )
105
+ errors.add :subject, 'relationship (or equivalent) already exists.'
106
+ return false
107
+ end
96
108
  end
97
- end
98
109
 
99
- # check for sub-properties
100
- TrippleNavigation.relation_and_all_subproperties(
101
- predicate_id) do |sub_prop|
102
- unless (Connection.all( :conditions => [
103
- "subject_id = ? AND predicate_id = ? AND obj_id = ?",
104
- subject_id, sub_prop, obj_id ] ).empty? )
105
- errors.add :subject, 'equivalent relationship already exists.'
106
- return false
110
+ # check for sub-properties
111
+ TrippleNavigation.relation_and_all_subproperties(
112
+ predicate_id) do |sub_prop|
113
+ unless (Connection.all( :conditions => [
114
+ "subject_id = ? AND predicate_id = ? AND obj_id = ?",
115
+ subject_id, sub_prop, obj_id ] ).empty? )
116
+ errors.add :subject, 'equivalent relationship already exists.'
117
+ return false
118
+ end
107
119
  end
108
- end
109
120
 
110
121
 
111
- # is there an existing implied opposing connection?
112
- # find all connections back-connecting our subject and object, determine
113
- # if any of their predicates has an (inherited)
114
- # inverse_relationship to current predicate
115
- if connections = Connection.all( :conditions => [
116
- "subject_id = ? AND obj_id = ?", obj_id, subject_id ] )
122
+ # is there an existing implied opposing connection?
123
+ # find all connections back-connecting our subject and object, determine
124
+ # if any of their predicates has an (inherited)
125
+ # inverse_relationship to current predicate
126
+ if connections = Connection.all( :conditions => [
127
+ "subject_id = ? AND obj_id = ?", obj_id, subject_id ] )
117
128
 
118
- inverse_id = Item.find_by_name("inverse_relationship").id
129
+ inverse_id = Item.find_by_name("inverse_relationship").id
119
130
 
120
- connections.each do |e|
121
- TrippleNavigation.relation_and_all_superproperties(
122
- predicate_id) do |proposed_rel|
131
+ connections.each do |e|
123
132
  TrippleNavigation.relation_and_all_superproperties(
124
- e.predicate_id) do |existing_rel|
125
- if Connection.all( :conditions => [
126
- "subject_id = ? AND predicate_id = ? AND obj_id = ?",
127
- proposed_rel, inverse_id, existing_rel ] ).length > 0
128
- errors.add :subject, 'already has this relationship implied.'
129
- return false
133
+ predicate_id) do |proposed_rel|
134
+ TrippleNavigation.relation_and_all_superproperties(
135
+ e.predicate_id) do |existing_rel|
136
+ if Connection.all( :conditions => [
137
+ "subject_id = ? AND predicate_id = ? AND obj_id = ?",
138
+ proposed_rel, inverse_id, existing_rel ] ).length > 0
139
+ errors.add :subject, 'already has this relationship implied.'
140
+ return false
141
+ end
130
142
  end
131
143
  end
132
144
  end
133
145
  end
134
- end
135
146
 
136
147
 
137
- #used in many subsequent tests
138
- spo_id = Item.find_by_name("sub_property_of").id
148
+ #used in many subsequent tests
149
+ spo_id = Item.find_by_name("sub_property_of").id
139
150
 
140
151
 
141
- # would this create an "individual parent_of category" relationship?
142
- subItem = Item.find(subject_id)
143
- objItem = Item.find(obj_id)
144
- # check for "individual parent_of category"
145
- if subItem.sti_type == ItemHelper::ITEM_INDIVIDUAL_CLASS_NAME &&
146
- objItem.sti_type == ItemHelper::ITEM_CATEGORY_CLASS_NAME
147
- if TrippleNavigation.check_properties(
148
- :does => predicate_id,
149
- :inherit_from => Item.find_by_name("parent_of").id,
150
- :via => spo_id)
151
- errors.add :predicate,
152
- 'an individual cannot be the parent of a category.'
153
- return false
152
+ # would this create an "individual parent_of category" relationship?
153
+ subItem = Item.find(subject_id)
154
+ objItem = Item.find(obj_id)
155
+ # check for "individual parent_of category"
156
+ if subItem.sti_type == ItemHelper::ITEM_INDIVIDUAL_CLASS_NAME &&
157
+ objItem.sti_type == ItemHelper::ITEM_CATEGORY_CLASS_NAME
158
+ if TrippleNavigation.check_properties(
159
+ :does => predicate_id,
160
+ :inherit_from => Item.find_by_name("parent_of").id,
161
+ :via => spo_id)
162
+ errors.add :predicate,
163
+ 'an individual cannot be the parent of a category.'
164
+ return false
165
+ end
154
166
  end
155
- end
156
167
 
157
- # check for "category child_of individual"
158
- if subItem.sti_type == ItemHelper::ITEM_CATEGORY_CLASS_NAME &&
159
- objItem.sti_type == ItemHelper::ITEM_INDIVIDUAL_CLASS_NAME
160
- if TrippleNavigation.check_properties(
161
- :does => predicate_id,
162
- :inherit_from => Item.find_by_name("child_of").id,
163
- :via => spo_id)
164
- errors.add :predicate,
165
- 'a category cannot be the child of an individual.'
166
- return false
168
+ # check for "category child_of individual"
169
+ if subItem.sti_type == ItemHelper::ITEM_CATEGORY_CLASS_NAME &&
170
+ objItem.sti_type == ItemHelper::ITEM_INDIVIDUAL_CLASS_NAME
171
+ if TrippleNavigation.check_properties(
172
+ :does => predicate_id,
173
+ :inherit_from => Item.find_by_name("child_of").id,
174
+ :via => spo_id)
175
+ errors.add :predicate,
176
+ 'a category cannot be the child of an individual.'
177
+ return false
178
+ end
167
179
  end
168
- end
169
180
 
170
181
 
171
- # would this create a loop (including connection-to-self) of
172
- # hierarchical or ordered relationships?
173
- # if this is the kind of property we have to worry about?
174
- [
175
- Item.find_by_name("parent_of").id,
176
- Item.find_by_name("child_of").id,
177
- Item.find_by_name("sub_property_of").id,
178
- Item.find_by_name("predecessor_of").id,
179
- Item.find_by_name("successor_of").id
180
- ].each do |prop_id|
181
- if TrippleNavigation.check_properties(
182
- :does => predicate_id, :inherit_from => prop_id, :via => spo_id)
182
+ # would this create a loop (including connection-to-self) of
183
+ # hierarchical or ordered relationships?
184
+ # if this is the kind of property we have to worry about?
185
+ [
186
+ Item.find_by_name("parent_of").id,
187
+ Item.find_by_name("child_of").id,
188
+ Item.find_by_name("sub_property_of").id,
189
+ Item.find_by_name("predecessor_of").id,
190
+ Item.find_by_name("successor_of").id
191
+ ].each do |prop_id|
183
192
  if TrippleNavigation.check_properties(
184
- :does => obj_id, :link_to => subject_id,
185
- :through_children_of => prop_id )
186
- errors.add :subject, 'this relationship would create a loop.'
187
- return false
193
+ :does => predicate_id, :inherit_from => prop_id, :via => spo_id)
194
+ if TrippleNavigation.check_properties(
195
+ :does => obj_id, :link_to => subject_id,
196
+ :through_children_of => prop_id )
197
+ errors.add :subject, 'this relationship would create a loop.'
198
+ return false
199
+ end
188
200
  end
189
201
  end
190
- end
191
202
 
192
- # is this a "bad" connection-to-self?
193
- if subject_id == obj_id
194
- [ "inverse_relationship", "hierarchical_relationship",
195
- "ordered_relationship" ].each do |relation_name|
196
- if TrippleNavigation.check_properties(
197
- :does => predicate_id,
198
- :inherit_from => Item.find_by_name(relation_name).id,
199
- :via => spo_id )
200
- errors.add :subject,
201
- 'cannot create an ordered/hierarchical relationship from an item to itself'
203
+ # is this a "bad" connection-to-self?
204
+ if subject_id == obj_id
205
+ [ "inverse_relationship", "hierarchical_relationship",
206
+ "ordered_relationship" ].each do |relation_name|
207
+ if TrippleNavigation.check_properties(
208
+ :does => predicate_id,
209
+ :inherit_from => Item.find_by_name(relation_name).id,
210
+ :via => spo_id )
211
+ errors.add :subject,
212
+ 'cannot create an ordered/hierarchical relationship from an item to itself'
213
+ return false
214
+ end
215
+ end
216
+ end
217
+
218
+
219
+ elsif kind_of_obj == OBJECT_KIND_SCALAR
220
+ [ [subject_id, :subject], [predicate_id, :predicate],
221
+ [scalar_obj, :scalar_obj] ].each do |tocheck|
222
+ field, symbol = *tocheck
223
+ if field.nil?
224
+ errors.add symbol, "can't be blank."
202
225
  return false
203
226
  end
204
227
  end
228
+
229
+
230
+ else
231
+ errors.add :kind_of_obj, "isn't valid (#{kind_of_obj})"
232
+ return false
205
233
  end
206
234
 
207
235
 
@@ -43,8 +43,10 @@ class Item < ActiveRecord::Base
43
43
  DATA_IS_UNALTERABLE = 1
44
44
 
45
45
 
46
+ # support sub-classes for Individual, Category, Property, Qualified
46
47
  self.inheritance_column = "sti_type"
47
48
 
49
+
48
50
  # name
49
51
  validates_presence_of :name, :message => "Item's name cannot be blank."
50
52
  validates_length_of :name, :maximum => 80,
@@ -83,4 +85,98 @@ class Item < ActiveRecord::Base
83
85
  # column default specified in the database schema
84
86
  self[:flags] or 0
85
87
  end
88
+
89
+
90
+ @class_item = nil
91
+ @class_has_been_set = false
92
+ after_save :after_save_callback # update class-defining connection after
93
+ # main object saves; rollback still works
94
+ def class_item
95
+ if @class_has_been_set
96
+ @class_item # ignore db content if this has been set
97
+ # explicitly. This will eventually be
98
+ # saved or discarded
99
+ else
100
+ iio_item = Item.find_by_name('is_instance_of')
101
+ class_assignment = Connection.first( :conditions => [
102
+ "subject_id = ? AND predicate_id = ?", id, iio_item.id ] )
103
+ if class_assignment.nil? or
104
+ class_assignment.kind_of_obj != Connection::OBJECT_KIND_ITEM
105
+ return nil
106
+ else
107
+ return class_assignment.obj
108
+ end
109
+ end
110
+ end
111
+ def class_item_id
112
+ item = class_item
113
+ return item.nil? ? nil : item.id
114
+ end
115
+ def class_item=( new_class_item )
116
+ @class_item = new_class_item
117
+ @class_has_been_set = true
118
+ end
119
+ def class_item_id=( new_class_id )
120
+ @class_item = new_class_id.nil? ? nil : Item.find_by_id( new_class_id )
121
+ @class_has_been_set = true
122
+ end
123
+
124
+ def is_class?
125
+ return( !( Connection.first( :conditions => [
126
+ "predicate_id = ? AND obj_id = ?",
127
+ Item.find_by_name('is_instance_of').id,
128
+ id ] ).nil? ) or
129
+ !( Connection.first( :conditions => [
130
+ "predicate_id = ? AND (subject_id = ? OR obj_id = ?)",
131
+ Item.find_by_name('sub_class_of').id,
132
+ id, id ] ).nil? )
133
+ )
134
+ end
135
+
136
+ def superclass_of
137
+ defining_connection = Connection.first( :conditions => [
138
+ "subject_id = ? AND predicate_id = ?",
139
+ id, Item.find_by_name('sub_class_of').id ] )
140
+ return defining_connection.obj unless defining_connection.nil?
141
+ return nil
142
+ end
143
+
144
+ def instance_of
145
+ connection = Connection.first( :conditions => [
146
+ "subject_id = ? AND predicate_id = ?",
147
+ id, Item.find_by_name('is_instance_of').id ] )
148
+ return nil if connection == nil
149
+ return connection.obj
150
+ end
151
+
152
+ private
153
+
154
+ def after_save_callback
155
+ if @class_has_been_set
156
+ iio_item = Item.find_by_name('is_instance_of')
157
+ class_assignment = Connection.first( :conditions => [
158
+ "subject_id = ? AND predicate_id = ?", id, iio_item.id ] )
159
+ if class_assignment && @class_item == class_assignment.obj
160
+ return true # class-defining connection already in db -> done
161
+ end
162
+
163
+ if class_assignment # if we're here, current db is different
164
+ class_assignment.destroy # so get rid of it
165
+ end
166
+
167
+ begin
168
+ class_assignment = Connection.new( {
169
+ :subject_id => id,
170
+ :predicate_id => iio_item.id,
171
+ :obj_id => @class_item.id,
172
+ :kind_of_obj => Connection::OBJECT_KIND_ITEM } )
173
+ @class_has_been_set = false
174
+ return class_assignment.save
175
+ rescue
176
+ return false
177
+ end
178
+ end
179
+
180
+ return true
181
+ end
86
182
  end