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
@@ -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