dryml 1.1.0.pre0
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/CHANGES.txt +9 -0
- data/LICENSE.txt +22 -0
- data/README +36 -0
- data/Rakefile +47 -0
- data/TODO.txt +6 -0
- data/lib/dryml/dryml_builder.rb +140 -0
- data/lib/dryml/dryml_doc.rb +155 -0
- data/lib/dryml/dryml_generator.rb +271 -0
- data/lib/dryml/dryml_support_controller.rb +13 -0
- data/lib/dryml/helper.rb +69 -0
- data/lib/dryml/parser/attribute.rb +41 -0
- data/lib/dryml/parser/base_parser.rb +254 -0
- data/lib/dryml/parser/document.rb +57 -0
- data/lib/dryml/parser/element.rb +27 -0
- data/lib/dryml/parser/elements.rb +21 -0
- data/lib/dryml/parser/source.rb +58 -0
- data/lib/dryml/parser/text.rb +13 -0
- data/lib/dryml/parser/tree_parser.rb +67 -0
- data/lib/dryml/parser.rb +3 -0
- data/lib/dryml/part_context.rb +133 -0
- data/lib/dryml/scoped_variables.rb +42 -0
- data/lib/dryml/static_tags +98 -0
- data/lib/dryml/tag_parameters.rb +32 -0
- data/lib/dryml/taglib.rb +124 -0
- data/lib/dryml/template.rb +1021 -0
- data/lib/dryml/template_environment.rb +613 -0
- data/lib/dryml/template_handler.rb +187 -0
- data/lib/dryml.rb +291 -0
- data/taglibs/core.dryml +136 -0
- data/test/dryml.rdoctest +68 -0
- metadata +131 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
module Dryml
|
2
|
+
|
3
|
+
# Raised when the part context fails its integrity check.
|
4
|
+
class PartContext
|
5
|
+
|
6
|
+
class TamperedWithPartContext < StandardError; end
|
7
|
+
|
8
|
+
class TypedId < String; end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :secret, :digest
|
12
|
+
end
|
13
|
+
self.digest = 'SHA1'
|
14
|
+
|
15
|
+
|
16
|
+
def self.client_side_storage(contexts, session)
|
17
|
+
return "" if contexts.empty?
|
18
|
+
|
19
|
+
contexts.map do |dom_id, context|
|
20
|
+
code = context.marshal(session).split("\n").map{|line| "'#{line}\\n'"}.join(" +\n ")
|
21
|
+
"hoboParts['#{dom_id}'] = (#{code});\n"
|
22
|
+
end.join
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def self.pre_marshal(x)
|
27
|
+
if x.is_a?(ActiveRecord::Base) && x.respond_to?(:typed_id)
|
28
|
+
TypedId.new(x.typed_id)
|
29
|
+
else
|
30
|
+
x
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def self.for_call(part_name, environment, locals)
|
36
|
+
new do |c|
|
37
|
+
c.part_name = part_name
|
38
|
+
c.locals = locals.map { |l| pre_marshal(l) }
|
39
|
+
c.this_id = environment.typed_id
|
40
|
+
c.form_field_path = environment.form_field_path
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def self.for_refresh(encoded_context, page_this, session)
|
46
|
+
new do |c|
|
47
|
+
c.unmarshal(encoded_context, page_this, session)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def initialize
|
53
|
+
yield self
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_accessor :part_name, :locals, :this, :this_field, :this_id, :form_field_path
|
57
|
+
|
58
|
+
|
59
|
+
def marshal(session)
|
60
|
+
context = [@part_name, @this_id, @locals]
|
61
|
+
context << form_field_path if form_field_path
|
62
|
+
data = Base64.encode64(Marshal.dump(context)).strip
|
63
|
+
digest = generate_digest(data, session)
|
64
|
+
"#{data}--#{digest}"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# Unmarshal part context to a hash and verify its integrity.
|
69
|
+
def unmarshal(client_store, page_this, session)
|
70
|
+
data, digest = CGI.unescape(client_store).strip.split('--')
|
71
|
+
|
72
|
+
raise TamperedWithPartContext unless digest == generate_digest(data, session)
|
73
|
+
|
74
|
+
context = Marshal.load(Base64.decode64(data))
|
75
|
+
|
76
|
+
part_name, this_id, locals, form_field_path = context
|
77
|
+
|
78
|
+
if RAILS_DEFAULT_LOGGER
|
79
|
+
RAILS_DEFAULT_LOGGER.info "Call part: #{part_name}. this-id = #{this_id}, locals = #{locals.inspect}"
|
80
|
+
RAILS_DEFAULT_LOGGER.info " : form_field_path = #{form_field_path.inspect}" if form_field_path
|
81
|
+
end
|
82
|
+
|
83
|
+
self.part_name = part_name
|
84
|
+
self.this_id = this_id
|
85
|
+
self.locals = restore_locals(locals)
|
86
|
+
self.form_field_path = form_field_path
|
87
|
+
|
88
|
+
parse_this_id(page_this)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# Generate the HMAC keyed message digest. Uses SHA1 by default.
|
93
|
+
def generate_digest(data, session)
|
94
|
+
secret = self.class.secret || ActionController::Base.session_options[:secret] || ActionController::Base.cached_session_options.first[:secret]
|
95
|
+
key = secret.respond_to?(:call) ? secret.call(session) : secret
|
96
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(self.class.digest), key, data)
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
def parse_this_id(page_this)
|
102
|
+
if this_id == "this"
|
103
|
+
self.this = page_this
|
104
|
+
elsif this_id =~ /^this:(.*)/
|
105
|
+
self.this = page_this
|
106
|
+
self.this_field = $1
|
107
|
+
elsif this_id == "nil"
|
108
|
+
nil
|
109
|
+
else
|
110
|
+
parts = this_id.split(':')
|
111
|
+
if parts.length == 3
|
112
|
+
self.this = Hobo::Model.find_by_typed_id("#{parts[0]}:#{parts[1]}")
|
113
|
+
self.this_field = parts[2]
|
114
|
+
else
|
115
|
+
self.this = Hobo::Model.find_by_typed_id(this_id)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
def restore_locals(locals)
|
122
|
+
locals.map do |l|
|
123
|
+
if l.is_a?(TypedId)
|
124
|
+
Hobo::Model.find_by_typed_id(this_id)
|
125
|
+
else
|
126
|
+
l
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Dryml
|
2
|
+
|
3
|
+
class ScopedVariables
|
4
|
+
|
5
|
+
def initialize(variables=nil)
|
6
|
+
@scopes = variables ? [variables] : []
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](key)
|
10
|
+
s = scope_with_key(key) and s[key]
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(key, val)
|
14
|
+
s = scope_with_key(key) or raise ArgumentError, "no such scoped variable: #{key}"
|
15
|
+
s[key] = val
|
16
|
+
end
|
17
|
+
|
18
|
+
def new_scope(variables)
|
19
|
+
@scopes << variables.dup
|
20
|
+
res = yield
|
21
|
+
@scopes.pop
|
22
|
+
res
|
23
|
+
end
|
24
|
+
|
25
|
+
def scope_with_key(key)
|
26
|
+
@scopes.reverse_each do |s|
|
27
|
+
return s if s.has_key?(key)
|
28
|
+
end
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(name, *args)
|
33
|
+
if name.to_s =~ /=$/
|
34
|
+
self[name.to_s[0..-2].to_sym] = args.first
|
35
|
+
else
|
36
|
+
self[name]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
abbr
|
2
|
+
acronym
|
3
|
+
address
|
4
|
+
applet
|
5
|
+
article
|
6
|
+
audio
|
7
|
+
b
|
8
|
+
basefont
|
9
|
+
bdo
|
10
|
+
big
|
11
|
+
blockquote
|
12
|
+
body
|
13
|
+
button
|
14
|
+
canvas
|
15
|
+
caption
|
16
|
+
center
|
17
|
+
cite
|
18
|
+
code
|
19
|
+
colgroup
|
20
|
+
command
|
21
|
+
datagrid
|
22
|
+
datalist
|
23
|
+
dd
|
24
|
+
del
|
25
|
+
details
|
26
|
+
dfn
|
27
|
+
dialog
|
28
|
+
dir
|
29
|
+
div
|
30
|
+
dl
|
31
|
+
dt
|
32
|
+
em
|
33
|
+
embed
|
34
|
+
fieldset
|
35
|
+
figure
|
36
|
+
font
|
37
|
+
frameset
|
38
|
+
h1
|
39
|
+
h2
|
40
|
+
h3
|
41
|
+
h4
|
42
|
+
h5
|
43
|
+
h6
|
44
|
+
head
|
45
|
+
hgroup
|
46
|
+
i
|
47
|
+
iframe
|
48
|
+
ins
|
49
|
+
isindex
|
50
|
+
kbd
|
51
|
+
label
|
52
|
+
legend
|
53
|
+
li
|
54
|
+
map
|
55
|
+
mark
|
56
|
+
menu
|
57
|
+
meter
|
58
|
+
nav
|
59
|
+
noframes
|
60
|
+
noscript
|
61
|
+
object
|
62
|
+
ol
|
63
|
+
optgroup
|
64
|
+
option
|
65
|
+
output
|
66
|
+
p
|
67
|
+
pre
|
68
|
+
progress
|
69
|
+
q
|
70
|
+
rp
|
71
|
+
rt
|
72
|
+
ruby
|
73
|
+
s
|
74
|
+
samp
|
75
|
+
script
|
76
|
+
select
|
77
|
+
small
|
78
|
+
source
|
79
|
+
span
|
80
|
+
strike
|
81
|
+
strong
|
82
|
+
style
|
83
|
+
sub
|
84
|
+
sup
|
85
|
+
tbody
|
86
|
+
td
|
87
|
+
textarea
|
88
|
+
tfoot
|
89
|
+
th
|
90
|
+
thead
|
91
|
+
time
|
92
|
+
title
|
93
|
+
tr
|
94
|
+
tt
|
95
|
+
u
|
96
|
+
ul
|
97
|
+
var
|
98
|
+
video
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Dryml
|
2
|
+
|
3
|
+
class NoParameterError < RuntimeError; end
|
4
|
+
|
5
|
+
class TagParameters < Hash
|
6
|
+
|
7
|
+
def initialize(parameters, exclude_names=nil)
|
8
|
+
if exclude_names.blank?
|
9
|
+
update(parameters)
|
10
|
+
else
|
11
|
+
parameters.each_pair { |k, v| self[k] = v unless k.in?(exclude_names) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, default_content="")
|
16
|
+
if name.to_s =~ /\?$/
|
17
|
+
has_key?(name.to_s[0..-2].to_sym)
|
18
|
+
else
|
19
|
+
self[name]._?.call(default_content) || ""
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
undef_method :default
|
24
|
+
|
25
|
+
# Question: does this do anything? -Tom
|
26
|
+
def [](param_name)
|
27
|
+
fetch(param_name, nil)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/lib/dryml/taglib.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
module Dryml
|
2
|
+
|
3
|
+
class Taglib
|
4
|
+
|
5
|
+
@cache = {}
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def get(options)
|
10
|
+
src_file = taglib_filename(options)
|
11
|
+
taglib = @cache[src_file]
|
12
|
+
if taglib
|
13
|
+
taglib.reload
|
14
|
+
else
|
15
|
+
taglib = Taglib.new(src_file)
|
16
|
+
@cache[src_file] = taglib
|
17
|
+
end
|
18
|
+
taglib
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear_cache
|
22
|
+
@cache = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def taglib_filename(options)
|
28
|
+
plugin = options[:plugin]
|
29
|
+
rails_root = Object.const_defined?(:RAILS_ROOT) ? RAILS_ROOT : "."
|
30
|
+
base = if plugin == "dryml"
|
31
|
+
"#{DRYML_ROOT}/taglibs"
|
32
|
+
elsif plugin == "hobo"
|
33
|
+
"#{HOBO_ROOT}/taglibs"
|
34
|
+
elsif plugin
|
35
|
+
"#{rails_root}/vendor/plugins/#{plugin}/taglibs"
|
36
|
+
elsif options[:src] =~ /\//
|
37
|
+
"#{rails_root}/app/views"
|
38
|
+
elsif options[:template_dir] =~ /^#{HOBO_ROOT}/
|
39
|
+
options[:template_dir]
|
40
|
+
elsif options[:absolute_template_path]
|
41
|
+
options[:absolute_template_path]
|
42
|
+
else
|
43
|
+
"#{rails_root}/#{options[:template_dir].gsub(/^\//, '')}" # remove leading / if there is one
|
44
|
+
end
|
45
|
+
|
46
|
+
src = options[:src] || plugin
|
47
|
+
filename = "#{base}/#{src}.dryml"
|
48
|
+
raise DrymlException, "No such taglib: #{base} #{options.inspect} #{filename}" unless File.exists?(filename)
|
49
|
+
filename
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize(src_file)
|
55
|
+
@src_file = src_file
|
56
|
+
load
|
57
|
+
end
|
58
|
+
|
59
|
+
def reload
|
60
|
+
load if File.mtime(@src_file) > @last_load_time
|
61
|
+
end
|
62
|
+
|
63
|
+
def load
|
64
|
+
@module = Module.new do
|
65
|
+
|
66
|
+
@tag_attrs = {}
|
67
|
+
@tag_aliases = []
|
68
|
+
|
69
|
+
class << self
|
70
|
+
|
71
|
+
def included(base)
|
72
|
+
@tag_aliases.each do |tag, feature|
|
73
|
+
if base.respond_to? :alias_method_chain_on_include
|
74
|
+
base.alias_method_chain_on_include tag, feature
|
75
|
+
else
|
76
|
+
base.send(:alias_method_chain, tag, feature)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def _register_tag_attrs(tag, attrs)
|
82
|
+
@tag_attrs[tag] = attrs
|
83
|
+
end
|
84
|
+
attr_reader :tag_attrs
|
85
|
+
|
86
|
+
def alias_method_chain_on_include(tag, feature)
|
87
|
+
@tag_aliases << [tag, feature]
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
template = Template.new(File.read(@src_file), @module, @src_file)
|
94
|
+
template.compile([], [])
|
95
|
+
@last_load_time = File.mtime(@src_file)
|
96
|
+
end
|
97
|
+
|
98
|
+
def import_into(class_or_module, as)
|
99
|
+
if as
|
100
|
+
# Define a method on class_or_module named whatever 'as'
|
101
|
+
# is. The first time the method is called it creates and
|
102
|
+
# returns an object that provides the taglib's tags as
|
103
|
+
# methods. On subsequent calls the object is cached in an
|
104
|
+
# instance variable "@_#{as}_taglib"
|
105
|
+
|
106
|
+
taglib_module = @module
|
107
|
+
ivar = "@_#{as}_taglib"
|
108
|
+
class_or_module.send(:define_method, as) do
|
109
|
+
instance_variable_get(ivar) or begin
|
110
|
+
as_class = Class.new(TemplateEnvironment) { include taglib_module }
|
111
|
+
as_object = as_class.new
|
112
|
+
as_object.copy_instance_variables_from(self)
|
113
|
+
instance_variable_set(ivar, as_object)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
else
|
117
|
+
class_or_module.send(:include, @module)
|
118
|
+
class_or_module.tag_attrs.update(@module.tag_attrs) if @module.respond_to?(:tag_attrs)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|