зеркало из https://github.com/github/ruby.git
* eval.c (rb_call): try local method look-up first for fcall, then
normal method look-up. [ruby-talk:202564] * eval.c (rb_get_method_body): save local method cache separately. * eval.c (search_method): export info whether method is local or not. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@10578 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
c09cea5e1b
Коммит
58f09b0916
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
Fri Jul 21 04:17:22 2006 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||
|
||||
* eval.c (rb_call): try local method look-up first for fcall, then
|
||||
normal method look-up. [ruby-talk:202564]
|
||||
|
||||
* eval.c (rb_get_method_body): save local method cache separately.
|
||||
|
||||
* eval.c (search_method): export info whether method is local or
|
||||
not.
|
||||
|
||||
Thu Jul 20 20:27:07 2006 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||
|
||||
* object.c (rb_mod_attr): make Module#attr to be an alias to
|
||||
|
|
66
eval.c
66
eval.c
|
@ -235,7 +235,7 @@ typedef jmp_buf rb_jmpbuf_t;
|
|||
#include <sys/stat.h>
|
||||
|
||||
VALUE rb_cProc;
|
||||
static VALUE rb_cBinding;
|
||||
VALUE rb_cBinding;
|
||||
static VALUE proc_alloc(VALUE,struct BLOCK*,int);
|
||||
static VALUE proc_invoke(VALUE,VALUE,VALUE,VALUE,int);
|
||||
static VALUE proc_lambda(void);
|
||||
|
@ -245,7 +245,7 @@ static struct BLOCK *passing_block(VALUE,struct BLOCK*);
|
|||
static int block_orphan(struct BLOCK *data);
|
||||
|
||||
VALUE rb_cMethod;
|
||||
static VALUE rb_cUnboundMethod;
|
||||
VALUE rb_cUnboundMethod;
|
||||
static VALUE umethod_bind(VALUE, VALUE);
|
||||
static VALUE rb_mod_define_method(int, VALUE*, VALUE);
|
||||
NORETURN(static void rb_raise_jump(VALUE));
|
||||
|
@ -467,9 +467,10 @@ rb_undef_alloc_func(VALUE klass)
|
|||
#define LOOKUP_NORMAL 0
|
||||
#define LOOKUP_FCALL 1
|
||||
#define LOOKUP_NOSKIP 2
|
||||
#define LOOKUP_LOCAL 1
|
||||
|
||||
static NODE*
|
||||
search_method(VALUE klass, ID id, VALUE *origin, int flag)
|
||||
search_method(VALUE klass, ID id, VALUE *origin, int flag, int *out)
|
||||
{
|
||||
NODE *body;
|
||||
|
||||
|
@ -477,6 +478,7 @@ search_method(VALUE klass, ID id, VALUE *origin, int flag)
|
|||
if (st_lookup(RCLASS(ruby_frame->this_class)->m_tbl, id, (st_data_t *)&body) &&
|
||||
body->nd_noex == NOEX_LOCAL) {
|
||||
if (origin) *origin = ruby_frame->this_class;
|
||||
if (out) *out = LOOKUP_LOCAL;
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
@ -484,6 +486,7 @@ search_method(VALUE klass, ID id, VALUE *origin, int flag)
|
|||
if (st_lookup(RCLASS(klass)->m_tbl, id, (st_data_t *)&body) &&
|
||||
(flag == LOOKUP_NOSKIP || body->nd_noex != NOEX_LOCAL)) {
|
||||
if (origin) *origin = klass;
|
||||
if (out) *out = LOOKUP_NORMAL;
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
@ -499,8 +502,9 @@ rb_get_method_body(VALUE *klassp, ID *idp, int *noexp)
|
|||
NODE * volatile body;
|
||||
struct cache_entry *ent;
|
||||
int noex = *noexp;
|
||||
int lc;
|
||||
|
||||
if ((body = search_method(klass, id, &origin, noex)) == 0 || !body->nd_body) {
|
||||
if ((body = search_method(klass, id, &origin, noex, &lc)) == 0 || !body->nd_body) {
|
||||
/* store empty info in cache */
|
||||
ent = cache[noex] + EXPR1(klass, id);
|
||||
ent->klass = klass;
|
||||
|
@ -513,9 +517,10 @@ rb_get_method_body(VALUE *klassp, ID *idp, int *noexp)
|
|||
}
|
||||
|
||||
if (ruby_running) {
|
||||
VALUE c = (lc == LOOKUP_LOCAL) ? origin : klass;
|
||||
/* store in cache */
|
||||
ent = cache[noex] + EXPR1(klass, id);
|
||||
ent->klass = klass;
|
||||
ent = cache[lc] + EXPR1(c, id);
|
||||
ent->klass = c;
|
||||
ent->noex = body->nd_noex;
|
||||
if (noexp) *noexp = body->nd_noex;
|
||||
body = body->nd_body;
|
||||
|
@ -552,7 +557,7 @@ rb_get_method_body(VALUE *klassp, ID *idp, int *noexp)
|
|||
NODE*
|
||||
rb_method_node(VALUE klass, ID id)
|
||||
{
|
||||
int noex = 0;
|
||||
int noex = LOOKUP_NORMAL;
|
||||
struct cache_entry *ent;
|
||||
|
||||
ent = cache[0] + EXPR1(klass, id);
|
||||
|
@ -641,9 +646,9 @@ rb_export_method(VALUE klass, ID name, ID noex)
|
|||
if (klass == rb_cObject) {
|
||||
rb_secure(4);
|
||||
}
|
||||
body = search_method(klass, name, &origin, LOOKUP_NOSKIP);
|
||||
body = search_method(klass, name, &origin, LOOKUP_NOSKIP, 0);
|
||||
if (!body && TYPE(klass) == T_MODULE) {
|
||||
body = search_method(rb_cObject, name, &origin, LOOKUP_NOSKIP);
|
||||
body = search_method(rb_cObject, name, &origin, LOOKUP_NOSKIP, 0);
|
||||
}
|
||||
if (!body || !body->nd_body) {
|
||||
raise_undef(klass, name);
|
||||
|
@ -741,8 +746,8 @@ rb_attr(VALUE klass, ID id, int read, int write, int noex)
|
|||
VALUE ruby_errinfo = Qnil;
|
||||
extern int ruby_nerrs;
|
||||
|
||||
static VALUE rb_eLocalJumpError;
|
||||
static VALUE rb_eSysStackError;
|
||||
VALUE rb_eLocalJumpError;
|
||||
VALUE rb_eSysStackError;
|
||||
|
||||
extern VALUE ruby_top_self;
|
||||
|
||||
|
@ -1965,7 +1970,7 @@ rb_undef(VALUE klass, ID id)
|
|||
if (id == __id__ || id == __send__ || id == init) {
|
||||
rb_warn("undefining `%s' may cause serious problem", rb_id2name(id));
|
||||
}
|
||||
body = search_method(klass, id, &origin, LOOKUP_NOSKIP);
|
||||
body = search_method(klass, id, &origin, LOOKUP_NOSKIP, 0);
|
||||
if (!body || !body->nd_body) {
|
||||
const char *s0 = " class";
|
||||
VALUE c = klass;
|
||||
|
@ -2062,10 +2067,10 @@ rb_alias(VALUE klass, ID name, ID def)
|
|||
if (klass == rb_cObject) {
|
||||
rb_secure(4);
|
||||
}
|
||||
orig = search_method(klass, def, &origin, LOOKUP_NOSKIP);
|
||||
orig = search_method(klass, def, &origin, LOOKUP_NOSKIP, 0);
|
||||
if (!orig || !orig->nd_body) {
|
||||
if (TYPE(klass) == T_MODULE) {
|
||||
orig = search_method(rb_cObject, def, &origin, LOOKUP_NOSKIP);
|
||||
orig = search_method(rb_cObject, def, &origin, LOOKUP_NOSKIP, 0);
|
||||
}
|
||||
}
|
||||
if (!orig || !orig->nd_body) {
|
||||
|
@ -3746,7 +3751,7 @@ rb_eval(VALUE self, NODE *n)
|
|||
rb_id2name(node->nd_mid));
|
||||
}
|
||||
rb_frozen_class_p(ruby_cbase);
|
||||
body = search_method(ruby_cbase, node->nd_mid, &origin, LOOKUP_NOSKIP);
|
||||
body = search_method(ruby_cbase, node->nd_mid, &origin, LOOKUP_NOSKIP, 0);
|
||||
if (body){
|
||||
if (RTEST(ruby_verbose) && ruby_cbase == origin &&
|
||||
body->nd_cnt == 0 && body->nd_body) {
|
||||
|
@ -4147,7 +4152,7 @@ static VALUE
|
|||
rb_mod_public_method_defined(VALUE mod, VALUE mid)
|
||||
{
|
||||
ID id = rb_to_id(mid);
|
||||
int noex = 1;
|
||||
int noex = LOOKUP_NOSKIP;
|
||||
|
||||
if (rb_get_method_body(&mod, &id, &noex)) {
|
||||
if (VISI_CHECK(noex, NOEX_PUBLIC))
|
||||
|
@ -4186,7 +4191,7 @@ static VALUE
|
|||
rb_mod_private_method_defined(VALUE mod, VALUE mid)
|
||||
{
|
||||
ID id = rb_to_id(mid);
|
||||
int noex = 0;
|
||||
int noex = LOOKUP_NOSKIP;
|
||||
|
||||
if (rb_get_method_body(&mod, &id, &noex)) {
|
||||
if (VISI_CHECK(noex, NOEX_PRIVATE))
|
||||
|
@ -4225,7 +4230,7 @@ static VALUE
|
|||
rb_mod_protected_method_defined(VALUE mod, VALUE mid)
|
||||
{
|
||||
ID id = rb_to_id(mid);
|
||||
int noex = 1;
|
||||
int noex = LOOKUP_NOSKIP;
|
||||
|
||||
if (rb_get_method_body(&mod, &id, &noex)) {
|
||||
if (VISI_CHECK(noex, NOEX_PROTECTED))
|
||||
|
@ -5894,7 +5899,7 @@ rb_call(VALUE klass, VALUE recv, ID mid,
|
|||
NODE *body; /* OK */
|
||||
int noex;
|
||||
ID id = mid;
|
||||
struct cache_entry *ent;
|
||||
struct cache_entry *ent = 0;
|
||||
|
||||
if (!klass) {
|
||||
rb_raise(rb_eNotImpError, "method `%s' called on terminated object (%p)",
|
||||
|
@ -5904,7 +5909,7 @@ rb_call(VALUE klass, VALUE recv, ID mid,
|
|||
case CALLING_FCALL:
|
||||
case CALLING_VCALL:
|
||||
if (recv == ruby_frame->self) {
|
||||
noex = LOOKUP_FCALL;
|
||||
noex = LOOKUP_LOCAL;
|
||||
break;
|
||||
}
|
||||
/* fall thtough */
|
||||
|
@ -5914,8 +5919,19 @@ rb_call(VALUE klass, VALUE recv, ID mid,
|
|||
}
|
||||
|
||||
/* is it in the method cache? */
|
||||
ent = cache[noex] + EXPR1(klass, mid);
|
||||
if (ent->mid == mid && ent->klass == klass) {
|
||||
if (noex == LOOKUP_LOCAL) {
|
||||
ent = cache[LOOKUP_FCALL] + EXPR1(ruby_frame->this_class, mid);
|
||||
if (ent->mid != mid || ent->klass != ruby_frame->this_class) {
|
||||
ent = NULL;
|
||||
}
|
||||
}
|
||||
if (ent) {
|
||||
ent = cache[LOOKUP_NORMAL] + EXPR1(klass, mid);
|
||||
if (ent->mid != mid || ent->klass != klass) {
|
||||
ent = NULL;
|
||||
}
|
||||
}
|
||||
if (ent) {
|
||||
if (!ent->method)
|
||||
return method_missing(recv, mid, argc, argv, block,
|
||||
scope==CALLING_VCALL?CSTAT_VCALL:0);
|
||||
|
@ -7368,9 +7384,9 @@ rb_mod_modfunc(int argc, VALUE *argv, VALUE module)
|
|||
|
||||
id = rb_to_id(argv[i]);
|
||||
for (;;) {
|
||||
body = search_method(m, id, &m, LOOKUP_NOSKIP);
|
||||
body = search_method(m, id, &m, LOOKUP_NOSKIP, 0);
|
||||
if (body == 0) {
|
||||
body = search_method(rb_cObject, id, &m, LOOKUP_NOSKIP);
|
||||
body = search_method(rb_cObject, id, &m, LOOKUP_NOSKIP, 0);
|
||||
}
|
||||
if (body == 0 || body->nd_body == 0) {
|
||||
rb_bug("undefined method `%s'; can't happen", rb_id2name(id));
|
||||
|
@ -12693,7 +12709,7 @@ rb_thread_atfork(void)
|
|||
* 3: 15 16
|
||||
*/
|
||||
|
||||
static VALUE rb_cCont;
|
||||
VALUE rb_cCont;
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
|
|
272
lib/cgi-lib.rb
272
lib/cgi-lib.rb
|
@ -1,272 +0,0 @@
|
|||
warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: cgi-lib is deprecated after Ruby 1.8.1; use cgi instead"
|
||||
|
||||
=begin
|
||||
|
||||
= simple CGI support library
|
||||
|
||||
= example
|
||||
|
||||
== get form values
|
||||
|
||||
require "cgi-lib.rb"
|
||||
query = CGI.new
|
||||
query['field'] # <== value of 'field'
|
||||
query.keys # <== array of fields
|
||||
|
||||
and query has Hash class methods
|
||||
|
||||
|
||||
== get cookie values
|
||||
|
||||
require "cgi-lib.rb"
|
||||
query = CGI.new
|
||||
query.cookie['name'] # <== cookie value of 'name'
|
||||
query.cookie.keys # <== all cookie names
|
||||
|
||||
and query.cookie has Hash class methods
|
||||
|
||||
|
||||
== print HTTP header and HTML string to $>
|
||||
|
||||
require "cgi-lib.rb"
|
||||
CGI::print{
|
||||
CGI::tag("HTML"){
|
||||
CGI::tag("HEAD"){ CGI::tag("TITLE"){"TITLE"} } +
|
||||
CGI::tag("BODY"){
|
||||
CGI::tag("FORM", {"ACTION"=>"test.rb", "METHOD"=>"POST"}){
|
||||
CGI::tag("INPUT", {"TYPE"=>"submit", "VALUE"=>"submit"})
|
||||
} +
|
||||
CGI::tag("HR")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
== make raw cookie string
|
||||
|
||||
require "cgi-lib.rb"
|
||||
cookie1 = CGI::cookie({'name' => 'name',
|
||||
'value' => 'value',
|
||||
'path' => 'path', # optional
|
||||
'domain' => 'domain', # optional
|
||||
'expires' => Time.now, # optional
|
||||
'secure' => true # optional
|
||||
})
|
||||
|
||||
CGI::print("Content-Type: text/html", cookie1, cookie2){ "string" }
|
||||
|
||||
|
||||
== print HTTP header and string to $>
|
||||
|
||||
require "cgi-lib.rb"
|
||||
CGI::print{ "string" }
|
||||
# == CGI::print("Content-Type: text/html"){ "string" }
|
||||
CGI::print("Content-Type: text/html", cookie1, cookie2){ "string" }
|
||||
|
||||
|
||||
=== NPH (no-parse-header) mode
|
||||
|
||||
require "cgi-lib.rb"
|
||||
CGI::print("nph"){ "string" }
|
||||
# == CGI::print("nph", "Content-Type: text/html"){ "string" }
|
||||
CGI::print("nph", "Content-Type: text/html", cookie1, cookie2){ "string" }
|
||||
|
||||
|
||||
== make HTML tag string
|
||||
|
||||
require "cgi-lib.rb"
|
||||
CGI::tag("element", {"attribute_name"=>"attribute_value"}){"content"}
|
||||
|
||||
|
||||
== make HTTP header string
|
||||
|
||||
require "cgi-lib.rb"
|
||||
CGI::header # == CGI::header("Content-Type: text/html")
|
||||
CGI::header("Content-Type: text/html", cookie1, cookie2)
|
||||
|
||||
|
||||
=== NPH (no-parse-header) mode
|
||||
|
||||
CGI::header("nph") # == CGI::header("nph", "Content-Type: text/html")
|
||||
CGI::header("nph", "Content-Type: text/html", cookie1, cookie2)
|
||||
|
||||
|
||||
== escape url encode
|
||||
|
||||
require "cgi-lib.rb"
|
||||
url_encoded_string = CGI::escape("string")
|
||||
|
||||
|
||||
== unescape url encoded
|
||||
|
||||
require "cgi-lib.rb"
|
||||
string = CGI::unescape("url encoded string")
|
||||
|
||||
|
||||
== escape HTML &"<>
|
||||
|
||||
require "cgi-lib.rb"
|
||||
CGI::escapeHTML("string")
|
||||
|
||||
|
||||
=end
|
||||
|
||||
require "delegate"
|
||||
|
||||
class CGI < SimpleDelegator
|
||||
|
||||
CR = "\015"
|
||||
LF = "\012"
|
||||
EOL = CR + LF
|
||||
|
||||
RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ]
|
||||
RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
|
||||
|
||||
# make rfc1123 date string
|
||||
def CGI::rfc1123_date(time)
|
||||
t = time.clone.gmtime
|
||||
return format("%s, %.2d %s %d %.2d:%.2d:%.2d GMT",
|
||||
RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
|
||||
t.hour, t.min, t.sec)
|
||||
end
|
||||
|
||||
# escape url encode
|
||||
def CGI::escape(str)
|
||||
str.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) }
|
||||
end
|
||||
|
||||
# unescape url encoded
|
||||
def CGI::unescape(str)
|
||||
str.gsub(/\+/, ' ').gsub(/%([0-9a-fA-F]{2})/){ [$1.hex].pack("c") }
|
||||
end
|
||||
|
||||
# escape HTML
|
||||
def CGI::escapeHTML(str)
|
||||
str.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/</, "<")
|
||||
end
|
||||
|
||||
# offline mode. read name=value pairs on standard input.
|
||||
def read_from_cmdline
|
||||
require "shellwords.rb"
|
||||
words = Shellwords.shellwords(
|
||||
if not ARGV.empty?
|
||||
ARGV.join(' ')
|
||||
else
|
||||
STDERR.print "(offline mode: enter name=value pairs on standard input)\n" if STDIN.tty?
|
||||
readlines.join(' ').gsub(/\n/, '')
|
||||
end.gsub(/\\=/, '%3D').gsub(/\\&/, '%26'))
|
||||
|
||||
if words.find{|x| x =~ /=/} then words.join('&') else words.join('+') end
|
||||
end
|
||||
|
||||
def initialize(input = $stdin)
|
||||
|
||||
@inputs = {}
|
||||
@cookie = {}
|
||||
|
||||
case ENV['REQUEST_METHOD']
|
||||
when "GET"
|
||||
ENV['QUERY_STRING'] or ""
|
||||
when "POST"
|
||||
input.read(Integer(ENV['CONTENT_LENGTH'])) or ""
|
||||
else
|
||||
read_from_cmdline
|
||||
end.split(/[&;]/).each do |x|
|
||||
key, val = x.split(/=/,2).collect{|x|CGI::unescape(x)}
|
||||
if @inputs.include?(key)
|
||||
@inputs[key] += "\0" + (val or "")
|
||||
else
|
||||
@inputs[key] = (val or "")
|
||||
end
|
||||
end
|
||||
|
||||
super(@inputs)
|
||||
|
||||
if ENV.has_key?('HTTP_COOKIE') or ENV.has_key?('COOKIE')
|
||||
(ENV['HTTP_COOKIE'] or ENV['COOKIE']).split(/; /).each do |x|
|
||||
key, val = x.split(/=/,2)
|
||||
key = CGI::unescape(key)
|
||||
val = val.split(/&/).collect{|x|CGI::unescape(x)}.join("\0")
|
||||
if @cookie.include?(key)
|
||||
@cookie[key] += "\0" + val
|
||||
else
|
||||
@cookie[key] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr("inputs")
|
||||
attr("cookie")
|
||||
|
||||
# make HTML tag string
|
||||
def CGI::tag(element, attributes = {})
|
||||
"<" + escapeHTML(element) + attributes.collect{|name, value|
|
||||
" " + escapeHTML(name) + '="' + escapeHTML(value) + '"'
|
||||
}.to_s + ">" +
|
||||
(iterator? ? yield.to_s + "</" + escapeHTML(element) + ">" : "")
|
||||
end
|
||||
|
||||
# make raw cookie string
|
||||
def CGI::cookie(options)
|
||||
"Set-Cookie: " + options['name'] + '=' + escape(options['value']) +
|
||||
(options['domain'] ? '; domain=' + options['domain'] : '') +
|
||||
(options['path'] ? '; path=' + options['path'] : '') +
|
||||
(options['expires'] ? '; expires=' + rfc1123_date(options['expires']) : '') +
|
||||
(options['secure'] ? '; secure' : '')
|
||||
end
|
||||
|
||||
# make HTTP header string
|
||||
def CGI::header(*options)
|
||||
if defined?(MOD_RUBY)
|
||||
options.each{|option|
|
||||
option.sub(/(.*?): (.*)/){
|
||||
Apache::request.headers_out[$1] = $2
|
||||
}
|
||||
}
|
||||
Apache::request.send_http_header
|
||||
''
|
||||
else
|
||||
if options.delete("nph") or (ENV['SERVER_SOFTWARE'] =~ /IIS/)
|
||||
[(ENV['SERVER_PROTOCOL'] or "HTTP/1.0") + " 200 OK",
|
||||
"Date: " + rfc1123_date(Time.now),
|
||||
"Server: " + (ENV['SERVER_SOFTWARE'] or ""),
|
||||
"Connection: close"] +
|
||||
(options.empty? ? ["Content-Type: text/html"] : options)
|
||||
else
|
||||
options.empty? ? ["Content-Type: text/html"] : options
|
||||
end.join(EOL) + EOL + EOL
|
||||
end
|
||||
end
|
||||
|
||||
# print HTTP header and string to $>
|
||||
def CGI::print(*options)
|
||||
$>.print CGI::header(*options) + yield.to_s
|
||||
end
|
||||
|
||||
# print message to $>
|
||||
def CGI::message(message, title = "", header = ["Content-Type: text/html"])
|
||||
if message.kind_of?(Hash)
|
||||
title = message['title']
|
||||
header = message['header']
|
||||
message = message['body']
|
||||
end
|
||||
CGI::print(*header){
|
||||
CGI::tag("HTML"){
|
||||
CGI::tag("HEAD"){ CGI.tag("TITLE"){ title } } +
|
||||
CGI::tag("BODY"){ message }
|
||||
}
|
||||
}
|
||||
true
|
||||
end
|
||||
|
||||
# print error message to $> and exit
|
||||
def CGI::error
|
||||
CGI::message({'title'=>'ERROR', 'body'=>
|
||||
CGI::tag("PRE"){
|
||||
"ERROR: " + CGI::tag("STRONG"){ escapeHTML($!.to_s) } + "\n" + escapeHTML($@.join("\n"))
|
||||
}
|
||||
})
|
||||
exit
|
||||
end
|
||||
end
|
|
@ -74,12 +74,10 @@ module RSS
|
|||
:media => "printer", :charset => "UTF-8",
|
||||
:alternate => "yes"},],
|
||||
].each do |xsss|
|
||||
p xsss
|
||||
doc = REXML::Document.new(make_sample_RDF)
|
||||
root = doc.root
|
||||
xsss.each do |xss|
|
||||
content = xss.collect do |key, name|
|
||||
p [key, name]
|
||||
%Q[#{key}="#{name}"]
|
||||
end.join(" ")
|
||||
pi = REXML::Instruction.new("xml-stylesheet", content)
|
||||
|
|
Загрузка…
Ссылка в новой задаче