smklib 1.0.6
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.
- checksums.yaml +7 -0
- data/app/assets/images/add.png +0 -0
- data/app/assets/images/button_cancel.png +0 -0
- data/app/assets/images/button_ok.png +0 -0
- data/app/assets/images/cmseditor/16x16/text_block.png +0 -0
- data/app/assets/images/cmseditor/16x16/text_bold.png +0 -0
- data/app/assets/images/cmseditor/16x16/text_center.png +0 -0
- data/app/assets/images/cmseditor/16x16/text_italic.png +0 -0
- data/app/assets/images/cmseditor/16x16/text_left.png +0 -0
- data/app/assets/images/cmseditor/16x16/text_right.png +0 -0
- data/app/assets/images/cmseditor/16x16/text_strike.png +0 -0
- data/app/assets/images/cmseditor/16x16/text_sub.png +0 -0
- data/app/assets/images/cmseditor/16x16/text_super.png +0 -0
- data/app/assets/images/cmseditor/16x16/text_under.png +0 -0
- data/app/assets/images/cmseditor/22x22/text_block.png +0 -0
- data/app/assets/images/cmseditor/22x22/text_bold.png +0 -0
- data/app/assets/images/cmseditor/22x22/text_center.png +0 -0
- data/app/assets/images/cmseditor/22x22/text_italic.png +0 -0
- data/app/assets/images/cmseditor/22x22/text_left.png +0 -0
- data/app/assets/images/cmseditor/22x22/text_right.png +0 -0
- data/app/assets/images/cmseditor/22x22/text_strike.png +0 -0
- data/app/assets/images/cmseditor/22x22/text_sub.png +0 -0
- data/app/assets/images/cmseditor/22x22/text_super.png +0 -0
- data/app/assets/images/cmseditor/22x22/text_under.png +0 -0
- data/app/assets/images/trashcan_empty.png +0 -0
- data/app/assets/javascripts/collapsable.js +56 -0
- data/app/assets/javascripts/hide_show.js +39 -0
- data/app/assets/javascripts/jscalendar-1.0/calendar-setup.js +200 -0
- data/app/assets/javascripts/jscalendar-1.0/calendar.js +1806 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-af.js +39 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-al.js +101 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-bg.js +124 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-big5-utf8.js +123 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-big5.js +123 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-br.js +108 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ca.js +123 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-cs-utf8.js +65 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-cs-win.js +65 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-da.js +123 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-de.js +124 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-du.js +45 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-el.js +89 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-en.js +127 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-es.js +129 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-fi.js +98 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-fr.js +125 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-he-utf8.js +123 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-hr-utf8.js +49 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-hr.js +0 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-hu.js +124 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-it.js +124 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-jp.js +45 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ko-utf8.js +120 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ko.js +120 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-lt-utf8.js +114 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-lt.js +114 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-lv.js +123 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-nl.js +73 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-no.js +114 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-pl-utf8.js +93 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-pl.js +56 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-pt.js +123 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ro.js +66 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ru.js +123 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ru_win_.js +123 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-si.js +94 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-sk.js +99 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-sp.js +110 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-sv.js +93 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-tr.js +58 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/calendar-zh.js +119 -0
- data/app/assets/javascripts/jscalendar-1.0/lang/cn_utf8.js +123 -0
- data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/active-bg.gif +0 -0
- data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/dark-bg.gif +0 -0
- data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/hover-bg.gif +0 -0
- data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/menuarrow.gif +0 -0
- data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/normal-bg.gif +0 -0
- data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/rowhover-bg.gif +0 -0
- data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/status-bg.gif +0 -0
- data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/theme.css +236 -0
- data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/title-bg.gif +0 -0
- data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/today-bg.gif +0 -0
- data/app/views/error_mailer/snapshot.rhtml +71 -0
- data/lib/smklib.rb +6 -0
- data/lib/smklib/array_ext.rb +76 -0
- data/lib/smklib/browsers.rb +69 -0
- data/lib/smklib/builder_ext.rb +103 -0
- data/lib/smklib/date_ext.rb +23 -0
- data/lib/smklib/debug_support.rb +27 -0
- data/lib/smklib/error_mailer.rb +70 -0
- data/lib/smklib/hash_ext.rb +19 -0
- data/lib/smklib/htmlutils.rb +604 -0
- data/lib/smklib/movable_children.rb +28 -0
- data/lib/smklib/object_ext.rb +34 -0
- data/lib/smklib/php_serialize.rb +309 -0
- data/lib/smklib/railtie.rb +6 -0
- data/lib/smklib/somekool_scaffold.rb +198 -0
- data/lib/smklib/super_looking_list.rb +63 -0
- data/lib/smklib/version.rb +11 -0
- data/vendor/calendar_helper.rb +100 -0
- data/vendor/htmltokenizer.rb +259 -0
- metadata +158 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
class ActiveRecord::Base
|
|
3
|
+
def self.has_movable(association_id, options = {}, &extension)
|
|
4
|
+
define_method('last_order_index') do
|
|
5
|
+
max = -6500
|
|
6
|
+
self.send(association_id.to_s).each { |p| max = p.order_index.to_i if p.order_index.to_i > max.to_i }
|
|
7
|
+
max < -1 ? -1 : max
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
define_method('update_order_indexes') do |moved_page_id, above_page_id|
|
|
11
|
+
moved_page = self.send(association_id.to_s).find(moved_page_id)
|
|
12
|
+
above_page = self.send(association_id.to_s).find(above_page_id)
|
|
13
|
+
next_next_order_index = above_page.order_index.to_i + 2
|
|
14
|
+
self.send(association_id.to_s).find(:all, :conditions => ['order_index > ?', above_page.order_index]).each { |p|
|
|
15
|
+
p.order_index = next_next_order_index
|
|
16
|
+
p.save
|
|
17
|
+
next_next_order_index +=1
|
|
18
|
+
}
|
|
19
|
+
moved_page.order_index = above_page.order_index.to_i + 1
|
|
20
|
+
moved_page.save
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.is_movable_within_a(association_id, options = {}, &extension)
|
|
26
|
+
before_create { |model| model.order_index = model.send(association_id.to_s).last_order_index + 1 }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
|
|
3
|
+
def self.run_once(unique_string, &block)
|
|
4
|
+
$run_once_guard ||= {}
|
|
5
|
+
return if $run_once_guard[unique_string]
|
|
6
|
+
raise "No block given" unless block_given?
|
|
7
|
+
yield
|
|
8
|
+
$run_once_guard[unique_string] = true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_class
|
|
12
|
+
self.class.const_get self.to_s
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def in?(array)
|
|
16
|
+
array.include? self
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# based on http://www.rubygarden.org/ruby?Make_A_Deep_Copy_Of_An_Object
|
|
20
|
+
def deep_clone
|
|
21
|
+
Marshal::load(Marshal.dump(self))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def ignore_exception(*exceptions, &block)
|
|
25
|
+
begin
|
|
26
|
+
yield
|
|
27
|
+
rescue Exception => e
|
|
28
|
+
skip = false
|
|
29
|
+
exceptions.each { |ex| skip = true if e.kind_of? ex }
|
|
30
|
+
raise e unless skip
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# License and Copyright {{{
|
|
3
|
+
# Copyright (c) 2003 Thomas Hurst <freaky@aagh.net>
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
# }}}
|
|
23
|
+
|
|
24
|
+
# PHP serialize() and unserialize() workalikes
|
|
25
|
+
# First Released: 2003-06-02 (1.0.0)
|
|
26
|
+
# Prev Release: 2003-06-16 (1.0.1), by Thomas Hurst <tom@hur.st>
|
|
27
|
+
# This Release: 2004-09-17 (1.0.2), by Thomas Hurst <tom@hur.st>
|
|
28
|
+
# Switch all {}'s to explicit Hash.new's.
|
|
29
|
+
#
|
|
30
|
+
# These two methods should, for the most part, be functionally identical
|
|
31
|
+
# to the respective PHP functions;
|
|
32
|
+
# http://www.php.net/serialize, http://www.php.net/unserialize
|
|
33
|
+
#
|
|
34
|
+
#
|
|
35
|
+
# string = PHP.serialize(mixed var[, bool assoc])
|
|
36
|
+
# Returns a string representing the argument in a form PHP.unserialize
|
|
37
|
+
# and PHP's unserialize() should both be able to load.
|
|
38
|
+
#
|
|
39
|
+
# Array, Hash, Fixnum, Float, True/FalseClass, NilClass, String and Struct
|
|
40
|
+
# are supported; as are objects which support the to_assoc method, which
|
|
41
|
+
# returns an array of the form [['attr_name', 'value']..]. Anything else
|
|
42
|
+
# will raise a TypeError.
|
|
43
|
+
#
|
|
44
|
+
# If 'assoc' is specified, Array's who's first element is a two value
|
|
45
|
+
# array will be assumed to be an associative array, and will be serialized
|
|
46
|
+
# as a PHP associative array rather than a multidimensional array.
|
|
47
|
+
#
|
|
48
|
+
#
|
|
49
|
+
#
|
|
50
|
+
# mixed = PHP.unserialize(string serialized, [hash classmap, [bool assoc]])
|
|
51
|
+
# Returns an object containing the reconstituted data from serialized.
|
|
52
|
+
#
|
|
53
|
+
# If a PHP array (associative; like an ordered hash) is encountered, it
|
|
54
|
+
# scans the keys; if they're all incrementing integers counting from 0,
|
|
55
|
+
# it's unserialized as an Array, otherwise it's unserialized as a Hash.
|
|
56
|
+
# Note: this will lose ordering. To avoid this, specify assoc=true,
|
|
57
|
+
# and it will be unserialized as an associative array: [[key,value],...]
|
|
58
|
+
#
|
|
59
|
+
# If a serialized object is encountered, the hash 'classmap' is searched for
|
|
60
|
+
# the class name (as a symbol). Since PHP classnames are not case-preserving,
|
|
61
|
+
# this *must* be a .capitalize()d representation. The value is expected
|
|
62
|
+
# to be the class itself; i.e. something you could call .new on.
|
|
63
|
+
#
|
|
64
|
+
# If it's not found in 'classmap', the current constant namespace is searched,
|
|
65
|
+
# and failing that, a new Struct(classname) is generated, with the arguments
|
|
66
|
+
# for .new specified in the same order PHP provided; since PHP uses hashes
|
|
67
|
+
# to represent attributes, this should be the same order they're specified
|
|
68
|
+
# in PHP, but this is untested.
|
|
69
|
+
#
|
|
70
|
+
# each serialized attribute is sent to the new object using the respective
|
|
71
|
+
# {attribute}=() method; you'll get a NameError if the method doesn't exist.
|
|
72
|
+
#
|
|
73
|
+
# Array, Hash, Fixnum, Float, True/FalseClass, NilClass and String should
|
|
74
|
+
# be returned identically (i.e. foo == PHP.unserialize(PHP.serialize(foo))
|
|
75
|
+
# for these types); Struct should be too, provided it's in the namespace
|
|
76
|
+
# Module.const_get within unserialize() can see, or you gave it the same
|
|
77
|
+
# name in the Struct.new(<structname>), otherwise you should provide it in
|
|
78
|
+
# classmap.
|
|
79
|
+
#
|
|
80
|
+
# Note: StringIO is required for unserialize(); it's loaded as needed
|
|
81
|
+
|
|
82
|
+
module PHP
|
|
83
|
+
def PHP.serialize(var, assoc = false) # {{{
|
|
84
|
+
s = ''
|
|
85
|
+
case var
|
|
86
|
+
when Array
|
|
87
|
+
s << "a:#{var.size}:{"
|
|
88
|
+
if assoc and var.first.is_a?(Array) and var.first.size == 2
|
|
89
|
+
var.each { |k,v|
|
|
90
|
+
s << PHP.serialize(k) << PHP.serialize(v)
|
|
91
|
+
}
|
|
92
|
+
else
|
|
93
|
+
var.each_with_index { |v,i|
|
|
94
|
+
s << "i:#{i};#{PHP.serialize(v)}"
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
s << '}'
|
|
99
|
+
|
|
100
|
+
when Hash
|
|
101
|
+
s << "a:#{var.size}:{"
|
|
102
|
+
var.each do |k,v|
|
|
103
|
+
s << "#{PHP.serialize(k)}#{PHP.serialize(v)}"
|
|
104
|
+
end
|
|
105
|
+
s << '}'
|
|
106
|
+
|
|
107
|
+
when Struct
|
|
108
|
+
# encode as Object with same name
|
|
109
|
+
s << "O:#{var.class.to_s.length}:\"#{var.class.to_s.downcase}\":#{var.members.length}:{"
|
|
110
|
+
var.members.each do |member|
|
|
111
|
+
s << "#{PHP.serialize(member)}#{PHP.serialize(var[member])}"
|
|
112
|
+
end
|
|
113
|
+
s << '}'
|
|
114
|
+
|
|
115
|
+
when String
|
|
116
|
+
s << "s:#{var.length}:\"#{var}\";"
|
|
117
|
+
|
|
118
|
+
when Fixnum # PHP doesn't have bignums
|
|
119
|
+
s << "i:#{var};"
|
|
120
|
+
|
|
121
|
+
when Float
|
|
122
|
+
s << "d:#{var};"
|
|
123
|
+
|
|
124
|
+
when NilClass
|
|
125
|
+
s << 'N;'
|
|
126
|
+
|
|
127
|
+
when FalseClass, TrueClass
|
|
128
|
+
s << "b:#{var ? 1 :0};"
|
|
129
|
+
|
|
130
|
+
else
|
|
131
|
+
if var.respond_to?(:to_assoc)
|
|
132
|
+
v = var.to_assoc
|
|
133
|
+
# encode as Object with same name
|
|
134
|
+
s << "O:#{var.class.to_s.length}:\"#{var.class.to_s.downcase}\":#{v.length}:{"
|
|
135
|
+
v.each do |k,v|
|
|
136
|
+
s << "#{PHP.serialize(k.to_s)}#{PHP.serialize(v)}"
|
|
137
|
+
end
|
|
138
|
+
s << '}'
|
|
139
|
+
else
|
|
140
|
+
raise TypeError, "Unable to serialize type #{var.class}"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
s
|
|
145
|
+
end # }}}
|
|
146
|
+
|
|
147
|
+
def PHP.unserialize(string, classmap = nil, assoc = false) # {{{
|
|
148
|
+
require 'stringio'
|
|
149
|
+
string = StringIO.new(string)
|
|
150
|
+
def string.read_until(char)
|
|
151
|
+
val = ''
|
|
152
|
+
while (c = self.read(1)) != char
|
|
153
|
+
val << c
|
|
154
|
+
end
|
|
155
|
+
val
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
classmap ||= Hash.new
|
|
159
|
+
|
|
160
|
+
do_unserialize(string, classmap, assoc)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def PHP.do_unserialize(string, classmap, assoc)
|
|
164
|
+
val = nil
|
|
165
|
+
# determine a type
|
|
166
|
+
type = string.read(2)[0,1]
|
|
167
|
+
case type
|
|
168
|
+
when 'a' # associative array, a:length:{[index][value]...}
|
|
169
|
+
count = string.read_until('{').to_i
|
|
170
|
+
val = vals = Array.new
|
|
171
|
+
count.times do |i|
|
|
172
|
+
vals << [do_unserialize(string, classmap, assoc), do_unserialize(string, classmap, assoc)]
|
|
173
|
+
end
|
|
174
|
+
string.read(1) # skip the ending }
|
|
175
|
+
|
|
176
|
+
unless assoc
|
|
177
|
+
# now, we have an associative array, let's clean it up a bit...
|
|
178
|
+
# arrays have all numeric indexes, in order; otherwise we assume a hash
|
|
179
|
+
array = true
|
|
180
|
+
i = 0
|
|
181
|
+
vals.each do |key,value|
|
|
182
|
+
if key != i # wrong index -> assume hash
|
|
183
|
+
array = false
|
|
184
|
+
break
|
|
185
|
+
end
|
|
186
|
+
i += 1
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
if array
|
|
190
|
+
vals.collect! do |key,value|
|
|
191
|
+
value
|
|
192
|
+
end
|
|
193
|
+
else
|
|
194
|
+
val = Hash.new
|
|
195
|
+
vals.each do |key,value|
|
|
196
|
+
val[key] = value
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
when 'O' # object, O:length:"class":length:{[attribute][value]...}
|
|
202
|
+
# class name (lowercase in PHP, grr)
|
|
203
|
+
len = string.read_until(':').to_i + 3 # quotes, seperator
|
|
204
|
+
klass = string.read(len)[1...-2].capitalize.intern # read it, kill useless quotes
|
|
205
|
+
|
|
206
|
+
# read the attributes
|
|
207
|
+
attrs = []
|
|
208
|
+
len = string.read_until('{').to_i
|
|
209
|
+
|
|
210
|
+
len.times do
|
|
211
|
+
attr = (do_unserialize(string, classmap, assoc))
|
|
212
|
+
attrs << [attr.intern, (attr << '=').intern, do_unserialize(string, classmap, assoc)]
|
|
213
|
+
end
|
|
214
|
+
string.read(1)
|
|
215
|
+
|
|
216
|
+
val = nil
|
|
217
|
+
# See if we need to map to a particular object
|
|
218
|
+
if classmap.has_key?(klass)
|
|
219
|
+
val = classmap[klass].new
|
|
220
|
+
elsif Struct.const_defined?(klass) # Nope; see if there's a Struct
|
|
221
|
+
classmap[klass] = val = Struct.const_get(klass)
|
|
222
|
+
val = val.new
|
|
223
|
+
else # Nope; see if there's a Constant
|
|
224
|
+
begin
|
|
225
|
+
classmap[klass] = val = Module.const_get(klass)
|
|
226
|
+
|
|
227
|
+
val = val.new
|
|
228
|
+
rescue NameError # Nope; make a new Struct
|
|
229
|
+
classmap[klass] = val = Struct.new(klass.to_s, *attrs.collect { |v| v[0].to_s })
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
attrs.each do |attr,attrassign,v|
|
|
234
|
+
val.__send__(attrassign, v)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
when 's' # string, s:length:"data";
|
|
238
|
+
len = string.read_until(':').to_i + 3 # quotes, separator
|
|
239
|
+
val = string.read(len)[1...-2] # read it, kill useless quotes
|
|
240
|
+
|
|
241
|
+
when 'i' # integer, i:123
|
|
242
|
+
val = string.read_until(';').to_i
|
|
243
|
+
|
|
244
|
+
when 'd' # double (float), d:1.23
|
|
245
|
+
val = string.read_until(';').to_f
|
|
246
|
+
|
|
247
|
+
when 'N' # NULL, N;
|
|
248
|
+
val = nil
|
|
249
|
+
|
|
250
|
+
when 'b' # bool, b:0 or 1
|
|
251
|
+
val = (string.read(2)[0] == ?1 ? true : false)
|
|
252
|
+
|
|
253
|
+
else
|
|
254
|
+
raise TypeError, "Unable to unserialize type '#{type}'"
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
val
|
|
258
|
+
end # }}}
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
if $0 == __FILE__
|
|
263
|
+
require 'tempfile'
|
|
264
|
+
|
|
265
|
+
class Foo
|
|
266
|
+
attr_accessor :foo, :bar, :wibble
|
|
267
|
+
def initialize
|
|
268
|
+
@foo = 'a'
|
|
269
|
+
@bar = 'b'
|
|
270
|
+
@wibble = 'c'
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def to_assoc
|
|
274
|
+
[['foo', @foo], ['bar', @bar], ['wibble', @wibble]]
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
puts "The following should all be equivilent, taking into account our limitations."
|
|
279
|
+
puts " 1. Is the original Ruby data structure."
|
|
280
|
+
puts " >> Is our generated serialized string."
|
|
281
|
+
puts " << Is the serialized string after PHP's eaten it."
|
|
282
|
+
puts " 2. Is the reconstructed Ruby data structure"
|
|
283
|
+
puts "--"
|
|
284
|
+
T = Struct.new(:structures,:ass)
|
|
285
|
+
[
|
|
286
|
+
{5 => nil, "foo" => 1, "nana" => ["x","y","z"], 3 => false, "bar" => 2.4, 4 => true},
|
|
287
|
+
T.new('Kick','Dude!'),
|
|
288
|
+
['a','b','c',['z','y','x'],{'hash' => 'smoke'},"\"\\''\""],
|
|
289
|
+
"He said \"Damnit, I'm lame\", and then I kicked his ass.",
|
|
290
|
+
Foo.new
|
|
291
|
+
].each do |v|
|
|
292
|
+
print " 1. "
|
|
293
|
+
p v
|
|
294
|
+
out = PHP.serialize(v)
|
|
295
|
+
|
|
296
|
+
tmp = Tempfile.new('php')
|
|
297
|
+
tmp.print(out)
|
|
298
|
+
tmp.close
|
|
299
|
+
|
|
300
|
+
puts " >> #{out}"
|
|
301
|
+
ret = `php -r 'echo serialize(unserialize(join("\n", file("#{tmp.path}"))));'`
|
|
302
|
+
puts " << #{ret}"
|
|
303
|
+
print " 2. "
|
|
304
|
+
p PHP.unserialize(ret)
|
|
305
|
+
puts " == #{out == ret ? 'Perfect!' : 'Differ!'}", ''
|
|
306
|
+
tmp.close!
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
################################################
|
|
2
|
+
#
|
|
3
|
+
# Usage:
|
|
4
|
+
#
|
|
5
|
+
# somekool_scaffold :agents, :ignore_columns => [:last_edit_at], :foreign_keys => [{:key => :sub_of_agent_id, :model => Agent, :title => _('Sub of')}], :css_class => :table_list_view
|
|
6
|
+
#
|
|
7
|
+
#
|
|
8
|
+
#
|
|
9
|
+
#
|
|
10
|
+
#
|
|
11
|
+
################################################
|
|
12
|
+
|
|
13
|
+
module ActionView
|
|
14
|
+
module Helpers
|
|
15
|
+
module JavaScriptMacrosHelper
|
|
16
|
+
def in_place_collection_dropdown(field_id, options = {})
|
|
17
|
+
function = "new Ajax.InPlaceCollectionEditor("
|
|
18
|
+
function << "'#{field_id}', "
|
|
19
|
+
function << "'#{url_for(options[:url])}'"
|
|
20
|
+
|
|
21
|
+
js_options = {}
|
|
22
|
+
js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
|
|
23
|
+
js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
|
|
24
|
+
js_options['loadingText'] = %('#{options[:loading_text]}') if options[:loading_text]
|
|
25
|
+
#js_options['rows'] = options[:rows] if options[:rows]
|
|
26
|
+
#js_options['cols'] = options[:cols] if options[:cols]
|
|
27
|
+
#js_options['size'] = options[:size] if options[:size]
|
|
28
|
+
js_options['collection'] = options[:collection] if options[:collection]
|
|
29
|
+
js_options['value'] = options[:value] if options[:value]
|
|
30
|
+
js_options['externalControl'] = "'#{options[:external_control]}'" if options[:external_control]
|
|
31
|
+
js_options['loadTextURL'] = "'#{url_for(options[:load_text_url])}'" if options[:load_text_url]
|
|
32
|
+
js_options['ajaxOptions'] = options[:options] if options[:options]
|
|
33
|
+
js_options['evalScripts'] = options[:script] if options[:script]
|
|
34
|
+
js_options['callback'] = "function(form) { return #{options[:with]} }" if options[:with]
|
|
35
|
+
function << (', ' + options_for_javascript(js_options)) unless js_options.empty?
|
|
36
|
+
|
|
37
|
+
function << ')'
|
|
38
|
+
|
|
39
|
+
javascript_tag(function)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def in_place_editor_dropdown(object, method, tag_options = {}, in_place_editor_options = {})
|
|
44
|
+
tag = ::ActionView::Helpers::InstanceTag.new(object, method, self)
|
|
45
|
+
raise tag.inspect if tag_options[:crash]
|
|
46
|
+
front_val = tag_options.delete(:front_val)
|
|
47
|
+
tag_options = {:tag => "span", :id => "#{object}_#{method}_#{tag.object.id}_in_place_editor", :class => "in_place_editor_field"}.merge!(tag_options)
|
|
48
|
+
in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{object}_#{method}", :id => tag.object.id })
|
|
49
|
+
(front_val.to_s.empty? ? tag.to_content_tag(tag_options.delete(:tag), tag_options) : tag.content_tag(tag_options.delete(:tag), front_val, tag_options)) +
|
|
50
|
+
in_place_collection_dropdown(tag_options[:id], in_place_editor_options)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class ActionController::Base
|
|
57
|
+
# Get a metaclass for this class
|
|
58
|
+
#def self.metaclass; class << self; self; end; end
|
|
59
|
+
|
|
60
|
+
def self.somekool_scaffold(model, options = {})
|
|
61
|
+
#raise Object.const_get(model.to_s.singularize.capitalize).inspect
|
|
62
|
+
instance_eval do
|
|
63
|
+
# required for in_place_editor_field
|
|
64
|
+
include ActionView::Helpers::TagHelper
|
|
65
|
+
include ActionView::Helpers::JavaScriptHelper
|
|
66
|
+
include ActionView::Helpers::JavaScriptMacrosHelper
|
|
67
|
+
|
|
68
|
+
# get model object
|
|
69
|
+
model_sym = model.to_s.singularize
|
|
70
|
+
object_model = Object.const_get(model_sym.capitalize)
|
|
71
|
+
model_sym = model_sym.downcase.to_sym
|
|
72
|
+
|
|
73
|
+
# call in_plate_edit_for all columns
|
|
74
|
+
columns = object_model.content_columns.collect do |c|
|
|
75
|
+
if options[:ignore_columns].include? c.name.to_sym
|
|
76
|
+
nil
|
|
77
|
+
else
|
|
78
|
+
in_place_edit_for model_sym, c.name.to_sym
|
|
79
|
+
c
|
|
80
|
+
end
|
|
81
|
+
end.compact
|
|
82
|
+
options[:foreign_keys].each do |fk|
|
|
83
|
+
in_place_edit_for model_sym, fk[:key].to_sym
|
|
84
|
+
|
|
85
|
+
define_method("set_#{model_sym}_#{fk[:key]}".to_sym) do
|
|
86
|
+
a = object_model.find(params[:id])
|
|
87
|
+
a[fk[:key].to_sym] = params[:value]
|
|
88
|
+
a.save
|
|
89
|
+
render :text => (a[fk[:key].to_sym].nil? ? _('NULL') : "#{a[fk[:key].to_sym]} - #{fk[:model].find(a[fk[:key].to_sym]).to_s}".nbsp)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# simple action index
|
|
94
|
+
define_method(:index) do
|
|
95
|
+
redirect_to :action => 'list'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# main action
|
|
99
|
+
define_method(:list) do
|
|
100
|
+
xml = Builder::XmlMarkup.new(:indent=>2)
|
|
101
|
+
xml.a(:class => "#{model_sym}_add_link", :href => "/#{model_sym.to_s.pluralize}/add") do
|
|
102
|
+
xml.img(:title => 'add_button', :alt => 'add_button', :border => 0, :src => '/smklib/images/add.png')
|
|
103
|
+
xml << _('Add new %s') % model_sym.to_s
|
|
104
|
+
end
|
|
105
|
+
xml.table(:cellpadding => 0, :cellspacing => 0, :border => 0, :class => "somekool_scaffold list #{model} #{options[:css_class]}") do
|
|
106
|
+
xml.tr do
|
|
107
|
+
xml.th _('ID') unless options[:no_id]
|
|
108
|
+
columns.each do |c|
|
|
109
|
+
xml.th { xml << c.human_name.to_s.nbsp}
|
|
110
|
+
end
|
|
111
|
+
options[:foreign_keys].each do |fk|
|
|
112
|
+
fk[:collection] = ([[nil, _('NULL')]] + fk[:model].find_all.collect { |item| [item.id, item.to_s] }).inspect.gsub(/nil/, 'null')
|
|
113
|
+
xml.th fk[:title]
|
|
114
|
+
end if options[:foreign_keys]
|
|
115
|
+
xml.th { xml << _('Delete') } unless options[:no_delete]
|
|
116
|
+
end
|
|
117
|
+
object_model.find_all.each_with_index do |row, row_index|
|
|
118
|
+
instance_variable_set("@#{model_sym}", row)
|
|
119
|
+
xml.tr(:class => row_index % 2 == 0 ? 'even' : 'odd') do
|
|
120
|
+
xml.td do
|
|
121
|
+
xml << instance_variable_get("@#{model_sym}")[:id].to_s
|
|
122
|
+
end unless options[:no_id]
|
|
123
|
+
columns.each do |c|
|
|
124
|
+
xml.td do
|
|
125
|
+
#xml << row.send(c.name)
|
|
126
|
+
instance_variable_get("@#{model_sym}")[c.name.to_sym] = " " if instance_variable_get("@#{model_sym}")[c.name.to_sym].to_s.empty?
|
|
127
|
+
xml << in_place_editor_field(model_sym, c.name)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
options[:foreign_keys].each do |fk|
|
|
131
|
+
begin
|
|
132
|
+
front_val = "#{row[fk[:key]]} - #{fk[:model].find(row[fk[:key]]).to_s}".nbsp
|
|
133
|
+
rescue
|
|
134
|
+
front_val = _('NULL')
|
|
135
|
+
end
|
|
136
|
+
xml.td do
|
|
137
|
+
xml << in_place_editor_dropdown(model_sym, fk[:key], {:front_val => front_val}, {:value => row[fk[:key]], :collection => fk[:collection]})
|
|
138
|
+
end
|
|
139
|
+
#xml.td fk[:model].find(:first, :conditions => ["#{fk[:key]} = ?", row[fk[:key]]]).inspect ## same
|
|
140
|
+
end if options[:foreign_keys]
|
|
141
|
+
xml.td do
|
|
142
|
+
xml.a(:class => "#{model_sym}_delete_link", :href => "/#{model_sym.to_s.pluralize}/destroy/#{row.id}") do
|
|
143
|
+
xml.img(:title => 'delete_button', :alt => 'delete_button', :border => 0, :src => '/smklib/images/trashcan_empty.png')
|
|
144
|
+
end
|
|
145
|
+
end unless options[:no_delete]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
render :text => xml.target!, :layout => 'main'
|
|
150
|
+
end
|
|
151
|
+
define_method(:add) do
|
|
152
|
+
xml = Builder::XmlMarkup.new(:indent=>2)
|
|
153
|
+
xml.form(:action => "/#{model_sym.to_s.pluralize}/create") do
|
|
154
|
+
xml.table(:cellpadding => 0, :cellspacing => 0, :border => 0, :class => "somekool_scaffold add #{model} #{options[:css_class]}") do
|
|
155
|
+
columns.each do |c|
|
|
156
|
+
xml.tr do
|
|
157
|
+
xml.th { xml << c.human_name.to_s.nbsp}
|
|
158
|
+
xml.td { xml.input(:type => 'text', :name => "#{model_sym}[#{c.name}]") }
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
options[:foreign_keys].each do |fk|
|
|
162
|
+
fk[:collection] = ([[nil, _('NULL')]] + fk[:model].find_all.collect { |item| [item.id, item.to_s] })
|
|
163
|
+
xml.tr do
|
|
164
|
+
xml.th fk[:title]
|
|
165
|
+
xml.td do
|
|
166
|
+
xml.select(:name => "#{model_sym}[#{fk[:key]}]") do
|
|
167
|
+
fk[:collection].each do |opt|
|
|
168
|
+
xml.option(:value => opt[0]) { xml << opt[1] }
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end if options[:foreign_keys]
|
|
174
|
+
xml.tr do
|
|
175
|
+
xml.td(:colspan => 2) { xml.input(:type => 'submit', :value => _('Add'))}
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
render :text => xml.target!, :layout => 'main'
|
|
180
|
+
end
|
|
181
|
+
define_method(:create) do
|
|
182
|
+
object_model.create(params[model_sym])
|
|
183
|
+
flash[:notice] = _('New %s created') % model_sym.to_s
|
|
184
|
+
redirect_to_path "/#{model_sym.to_s.pluralize}/list"
|
|
185
|
+
end
|
|
186
|
+
#define_method(:edit) do
|
|
187
|
+
#end
|
|
188
|
+
#define_method(:update) do
|
|
189
|
+
#end
|
|
190
|
+
define_method(:destroy) do
|
|
191
|
+
object_model.find(params[:id]).destroy
|
|
192
|
+
flash[:notice] = _('%s %s has been destroyed') % [model_sym.to_s, params[:id].to_s]
|
|
193
|
+
redirect_to_path "/#{model_sym.to_s.pluralize}/list"
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|