dryml 1.1.0.pre0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|