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.
- data/Rakefile +9 -5
- data/VERSION.yml +2 -2
- data/app/controllers/admin_controller.rb +34 -11
- data/app/controllers/connections_controller.rb +8 -2
- data/app/controllers/items_controller.rb +213 -68
- data/app/helpers/format_helper.rb +22 -1
- data/app/helpers/items_helper.rb +43 -0
- data/app/models/connection.rb +128 -100
- data/app/models/item.rb +96 -0
- data/app/views/connections/_spo_select_controls.html.erb +65 -31
- data/app/views/connections/edit.html.erb +7 -2
- data/app/views/connections/index.html.erb +11 -4
- data/app/views/connections/show.html.erb +13 -5
- data/app/views/items/_class_select.html.erb +41 -0
- data/app/views/items/_content_examples.html.erb +21 -18
- data/app/views/items/_core_tasks.html.erb +40 -5
- data/app/views/items/_form_fields.html.erb +4 -4
- data/app/views/items/_inline_item_add_form.html.erb +48 -0
- data/app/views/items/_inline_scalar_add_form.html.erb +36 -0
- data/app/views/items/_most_populous_classes.html.erb +42 -0
- data/app/views/items/_type_select.html.erb +28 -26
- data/app/views/items/edit.html.erb +8 -2
- data/app/views/items/index.html.erb +5 -1
- data/app/views/items/new.html.erb +69 -54
- data/app/views/items/newpop.html.erb +18 -3
- data/app/views/items/show.html.erb +110 -7
- data/app/views/layouts/application.html.erb +2 -2
- data/app/views/layouts/base.html.erb +4 -2
- data/app/views/layouts/home.html.erb +2 -2
- data/assets/wontomedia-sample.rb +2 -0
- data/config/asset_packages.yml +1 -0
- data/config/cucumber.yml +11 -13
- data/db/fixtures/connections.yml +85 -4
- data/db/fixtures/items.yml +140 -8
- data/db/migrate/20100315135952_provide_scalar_objects.rb +32 -0
- data/db/migrate/20100321042343_add_timestamp_columns.rb +33 -0
- data/db/schema.rb +17 -11
- data/default-custom/app/views/items/home.html.erb +1 -1
- data/default-custom/public/stylesheets/wm.css +21 -4
- data/lib/helpers/connection_helper.rb +84 -1
- data/lib/helpers/item_helper.rb +16 -3
- data/lib/tasks/cucumber.rake +0 -2
- data/public/images/transparent_ltblue_background.png +0 -0
- data/public/images/{alert_background.png → transparent_white_background.png} +0 -0
- data/public/javascripts/forConnectionsForms.js +182 -41
- data/public/javascripts/forItemsForms.js +40 -5
- data/public/javascripts/forItemsShow.js +27 -0
- data/public/javascripts/itemCreatePopup.js +10 -3
- 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
|
data/app/helpers/items_helper.rb
CHANGED
@@ -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
|
data/app/models/connection.rb
CHANGED
@@ -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
|
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
|
-
#
|
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
|
-
|
78
|
-
[
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
129
|
+
inverse_id = Item.find_by_name("inverse_relationship").id
|
119
130
|
|
120
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
138
|
-
|
148
|
+
#used in many subsequent tests
|
149
|
+
spo_id = Item.find_by_name("sub_property_of").id
|
139
150
|
|
140
151
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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 =>
|
185
|
-
|
186
|
-
|
187
|
-
|
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
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
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
|
|
data/app/models/item.rb
CHANGED
@@ -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
|