mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* ext/psych/*: importing Psych to trunk
* test/psych/*: ditto * lib/psych/*: ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27079 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
a8a99a7379
commit
b9b923ca94
66 changed files with 6056 additions and 0 deletions
|
@ -1,3 +1,9 @@
|
|||
Mon Mar 29 06:47:25 2010 Aaron Patterson <aaron@tenderlovemaking.com>
|
||||
|
||||
* ext/psych/*: importing Psych to trunk
|
||||
* test/psych/*: ditto
|
||||
* lib/psych/*: ditto
|
||||
|
||||
Sun Mar 28 21:04:21 2010 NARUSE, Yui <naruse@ruby-lang.org>
|
||||
|
||||
* configure.in (ac_cv_func_snprintf, ac_cv_func_vsnprintf):
|
||||
|
|
413
ext/psych/emitter.c
Normal file
413
ext/psych/emitter.c
Normal file
|
@ -0,0 +1,413 @@
|
|||
#include <psych.h>
|
||||
|
||||
VALUE cPsychEmitter;
|
||||
static ID id_write;
|
||||
|
||||
static void emit(yaml_emitter_t * emitter, yaml_event_t * event)
|
||||
{
|
||||
if(!yaml_emitter_emit(emitter, event))
|
||||
rb_raise(rb_eRuntimeError, "%s", emitter->problem);
|
||||
}
|
||||
|
||||
static int writer(void *ctx, unsigned char *buffer, size_t size)
|
||||
{
|
||||
VALUE io = (VALUE)ctx;
|
||||
VALUE str = rb_str_new((const char *)buffer, (long)size);
|
||||
VALUE wrote = rb_funcall(io, id_write, 1, str);
|
||||
return (int)NUM2INT(wrote);
|
||||
}
|
||||
|
||||
static void dealloc(yaml_emitter_t * emitter)
|
||||
{
|
||||
yaml_emitter_delete(emitter);
|
||||
free(emitter);
|
||||
}
|
||||
|
||||
static VALUE allocate(VALUE klass)
|
||||
{
|
||||
yaml_emitter_t * emitter = malloc(sizeof(yaml_emitter_t));
|
||||
yaml_emitter_initialize(emitter);
|
||||
yaml_emitter_set_unicode(emitter, 1);
|
||||
yaml_emitter_set_indent(emitter, 2);
|
||||
|
||||
return Data_Wrap_Struct(klass, 0, dealloc, emitter);
|
||||
}
|
||||
|
||||
/* call-seq: Psych::Emitter.new(io)
|
||||
*
|
||||
* Create a new Psych::Emitter that writes to +io+.
|
||||
*/
|
||||
static VALUE initialize(VALUE self, VALUE io)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_emitter_set_output(emitter, writer, (void *)io);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.start_stream(encoding)
|
||||
*
|
||||
* Start a stream emission with +encoding+
|
||||
*
|
||||
* See Psych::Handler#start_stream
|
||||
*/
|
||||
static VALUE start_stream(VALUE self, VALUE encoding)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
Check_Type(encoding, T_FIXNUM);
|
||||
|
||||
yaml_event_t event;
|
||||
yaml_stream_start_event_initialize(&event, (yaml_encoding_t)NUM2INT(encoding));
|
||||
|
||||
emit(emitter, &event);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.end_stream
|
||||
*
|
||||
* End a stream emission
|
||||
*
|
||||
* See Psych::Handler#end_stream
|
||||
*/
|
||||
static VALUE end_stream(VALUE self)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_event_t event;
|
||||
yaml_stream_end_event_initialize(&event);
|
||||
|
||||
emit(emitter, &event);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.start_document(version, tags, implicit)
|
||||
*
|
||||
* Start a document emission with YAML +version+, +tags+, and an +implicit+
|
||||
* start.
|
||||
*
|
||||
* See Psych::Handler#start_document
|
||||
*/
|
||||
static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_version_directive_t version_directive;
|
||||
|
||||
Check_Type(version, T_ARRAY);
|
||||
|
||||
if(RARRAY_LEN(version) > 0) {
|
||||
VALUE major = rb_ary_entry(version, (long)0);
|
||||
VALUE minor = rb_ary_entry(version, (long)1);
|
||||
|
||||
version_directive.major = NUM2INT(major);
|
||||
version_directive.minor = NUM2INT(minor);
|
||||
}
|
||||
|
||||
yaml_tag_directive_t * head = NULL;
|
||||
yaml_tag_directive_t * tail = NULL;
|
||||
|
||||
if(RTEST(tags)) {
|
||||
int i = 0;
|
||||
|
||||
Check_Type(tags, T_ARRAY);
|
||||
|
||||
head = xcalloc((size_t)RARRAY_LEN(tags), sizeof(yaml_tag_directive_t));
|
||||
tail = head;
|
||||
|
||||
for(i = 0; i < RARRAY_LEN(tags); i++) {
|
||||
VALUE tuple = RARRAY_PTR(tags)[i];
|
||||
Check_Type(tuple, T_ARRAY);
|
||||
|
||||
if(RARRAY_LEN(tuple) < 2) {
|
||||
xfree(head);
|
||||
rb_raise(rb_eRuntimeError, "tag tuple must be of length 2");
|
||||
}
|
||||
|
||||
tail->handle = (yaml_char_t *)StringValuePtr(RARRAY_PTR(tuple)[0]);
|
||||
tail->prefix = (yaml_char_t *)StringValuePtr(RARRAY_PTR(tuple)[1]);
|
||||
|
||||
tail++;
|
||||
}
|
||||
}
|
||||
|
||||
yaml_event_t event;
|
||||
yaml_document_start_event_initialize(
|
||||
&event,
|
||||
(RARRAY_LEN(version) > 0) ? &version_directive : NULL,
|
||||
head,
|
||||
tail,
|
||||
imp ? 1 : 0
|
||||
);
|
||||
|
||||
emit(emitter, &event);
|
||||
|
||||
if(head) xfree(head);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.end_document(implicit)
|
||||
*
|
||||
* End a document emission with an +implicit+ ending.
|
||||
*
|
||||
* See Psych::Handler#end_document
|
||||
*/
|
||||
static VALUE end_document(VALUE self, VALUE imp)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_event_t event;
|
||||
yaml_document_end_event_initialize(&event, imp ? 1 : 0);
|
||||
|
||||
emit(emitter, &event);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.scalar(value, anchor, tag, plain, quoted, style)
|
||||
*
|
||||
* Emit a scalar with +value+, +anchor+, +tag+, and a +plain+ or +quoted+
|
||||
* string type with +style+.
|
||||
*
|
||||
* See Psych::Handler#scalar
|
||||
*/
|
||||
static VALUE scalar(
|
||||
VALUE self,
|
||||
VALUE value,
|
||||
VALUE anchor,
|
||||
VALUE tag,
|
||||
VALUE plain,
|
||||
VALUE quoted,
|
||||
VALUE style
|
||||
) {
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
Check_Type(value, T_STRING);
|
||||
|
||||
yaml_event_t event;
|
||||
yaml_scalar_event_initialize(
|
||||
&event,
|
||||
(yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
|
||||
(yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
|
||||
(yaml_char_t*)StringValuePtr(value),
|
||||
(int)RSTRING_LEN(value),
|
||||
plain ? 1 : 0,
|
||||
quoted ? 1 : 0,
|
||||
(yaml_scalar_style_t)NUM2INT(style)
|
||||
);
|
||||
|
||||
emit(emitter, &event);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.start_sequence(anchor, tag, implicit, style)
|
||||
*
|
||||
* Start emitting a sequence with +anchor+, a +tag+, +implicit+ sequence
|
||||
* start and end, along with +style+.
|
||||
*
|
||||
* See Psych::Handler#start_sequence
|
||||
*/
|
||||
static VALUE start_sequence(
|
||||
VALUE self,
|
||||
VALUE anchor,
|
||||
VALUE tag,
|
||||
VALUE implicit,
|
||||
VALUE style
|
||||
) {
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_event_t event;
|
||||
yaml_sequence_start_event_initialize(
|
||||
&event,
|
||||
(yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
|
||||
(yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
|
||||
implicit ? 1 : 0,
|
||||
(yaml_sequence_style_t)NUM2INT(style)
|
||||
);
|
||||
|
||||
emit(emitter, &event);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.end_sequence
|
||||
*
|
||||
* End sequence emission.
|
||||
*
|
||||
* See Psych::Handler#end_sequence
|
||||
*/
|
||||
static VALUE end_sequence(VALUE self)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_event_t event;
|
||||
yaml_sequence_end_event_initialize(&event);
|
||||
|
||||
emit(emitter, &event);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.start_mapping(anchor, tag, implicit, style)
|
||||
*
|
||||
* Start emitting a YAML map with +anchor+, +tag+, an +implicit+ start
|
||||
* and end, and +style+.
|
||||
*
|
||||
* See Psych::Handler#start_mapping
|
||||
*/
|
||||
static VALUE start_mapping(
|
||||
VALUE self,
|
||||
VALUE anchor,
|
||||
VALUE tag,
|
||||
VALUE implicit,
|
||||
VALUE style
|
||||
) {
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_event_t event;
|
||||
yaml_mapping_start_event_initialize(
|
||||
&event,
|
||||
(yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
|
||||
(yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
|
||||
implicit ? 1 : 0,
|
||||
(yaml_sequence_style_t)NUM2INT(style)
|
||||
);
|
||||
|
||||
emit(emitter, &event);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.end_mapping
|
||||
*
|
||||
* Emit the end of a mapping.
|
||||
*
|
||||
* See Psych::Handler#end_mapping
|
||||
*/
|
||||
static VALUE end_mapping(VALUE self)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_event_t event;
|
||||
yaml_mapping_end_event_initialize(&event);
|
||||
|
||||
emit(emitter, &event);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.alias(anchor)
|
||||
*
|
||||
* Emit an alias with +anchor+.
|
||||
*
|
||||
* See Psych::Handler#alias
|
||||
*/
|
||||
static VALUE alias(VALUE self, VALUE anchor)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_event_t event;
|
||||
yaml_alias_event_initialize(
|
||||
&event,
|
||||
(yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor))
|
||||
);
|
||||
|
||||
emit(emitter, &event);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.canonical = true
|
||||
*
|
||||
* Set the output style to canonical, or not.
|
||||
*/
|
||||
static VALUE set_canonical(VALUE self, VALUE style)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_emitter_set_canonical(emitter, Qtrue == style ? 1 : 0);
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.canonical
|
||||
*
|
||||
* Get the output style, canonical or not.
|
||||
*/
|
||||
static VALUE canonical(VALUE self)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
return (emitter->canonical == 0) ? Qfalse : Qtrue;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.indentation = level
|
||||
*
|
||||
* Set the indentation level to +level+.
|
||||
*/
|
||||
static VALUE set_indentation(VALUE self, VALUE level)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
yaml_emitter_set_indent(emitter, NUM2INT(level));
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
/* call-seq: emitter.indentation
|
||||
*
|
||||
* Get the indentation level.
|
||||
*/
|
||||
static VALUE indentation(VALUE self)
|
||||
{
|
||||
yaml_emitter_t * emitter;
|
||||
Data_Get_Struct(self, yaml_emitter_t, emitter);
|
||||
|
||||
return INT2NUM(emitter->best_indent);
|
||||
}
|
||||
|
||||
void Init_psych_emitter()
|
||||
{
|
||||
VALUE psych = rb_define_module("Psych");
|
||||
VALUE handler = rb_define_class_under(psych, "Handler", rb_cObject);
|
||||
cPsychEmitter = rb_define_class_under(psych, "Emitter", handler);
|
||||
|
||||
rb_define_alloc_func(cPsychEmitter, allocate);
|
||||
|
||||
rb_define_method(cPsychEmitter, "initialize", initialize, 1);
|
||||
rb_define_method(cPsychEmitter, "start_stream", start_stream, 1);
|
||||
rb_define_method(cPsychEmitter, "end_stream", end_stream, 0);
|
||||
rb_define_method(cPsychEmitter, "start_document", start_document, 3);
|
||||
rb_define_method(cPsychEmitter, "end_document", end_document, 1);
|
||||
rb_define_method(cPsychEmitter, "scalar", scalar, 6);
|
||||
rb_define_method(cPsychEmitter, "start_sequence", start_sequence, 4);
|
||||
rb_define_method(cPsychEmitter, "end_sequence", end_sequence, 0);
|
||||
rb_define_method(cPsychEmitter, "start_mapping", start_mapping, 4);
|
||||
rb_define_method(cPsychEmitter, "end_mapping", end_mapping, 0);
|
||||
rb_define_method(cPsychEmitter, "alias", alias, 1);
|
||||
rb_define_method(cPsychEmitter, "canonical", canonical, 0);
|
||||
rb_define_method(cPsychEmitter, "canonical=", set_canonical, 1);
|
||||
rb_define_method(cPsychEmitter, "indentation", indentation, 0);
|
||||
rb_define_method(cPsychEmitter, "indentation=", set_indentation, 1);
|
||||
|
||||
id_write = rb_intern("write");
|
||||
}
|
||||
/* vim: set noet sws=4 sw=4: */
|
8
ext/psych/emitter.h
Normal file
8
ext/psych/emitter.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef PSYCH_EMITTER_H
|
||||
#define PSYCH_EMITTER_H
|
||||
|
||||
#include <psych.h>
|
||||
|
||||
void Init_psych_emitter();
|
||||
|
||||
#endif
|
22
ext/psych/extconf.rb
Normal file
22
ext/psych/extconf.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
require 'mkmf'
|
||||
|
||||
# :stopdoc:
|
||||
|
||||
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
|
||||
|
||||
INCLUDEDIR = Config::CONFIG['includedir']
|
||||
LIBDIR = Config::CONFIG['libdir']
|
||||
LIB_DIRS = ['/opt/local/lib', '/usr/local/lib', LIBDIR, '/usr/lib']
|
||||
libyaml = dir_config 'libyaml', '/opt/local/include', '/opt/local/lib'
|
||||
|
||||
def asplode missing
|
||||
abort "#{missing} is missing. Try 'port install libyaml +universal' " +
|
||||
"or 'yum install libyaml-devel'"
|
||||
end
|
||||
|
||||
asplode('yaml.h') unless find_header 'yaml.h'
|
||||
asplode('libyaml') unless find_library 'yaml', 'yaml_get_version'
|
||||
|
||||
create_makefile 'psych/psych'
|
||||
|
||||
# :startdoc:
|
314
ext/psych/parser.c
Normal file
314
ext/psych/parser.c
Normal file
|
@ -0,0 +1,314 @@
|
|||
#include <psych.h>
|
||||
|
||||
VALUE cPsychParser;
|
||||
VALUE ePsychSyntaxError;
|
||||
|
||||
static ID id_read;
|
||||
static ID id_empty;
|
||||
static ID id_start_stream;
|
||||
static ID id_end_stream;
|
||||
static ID id_start_document;
|
||||
static ID id_end_document;
|
||||
static ID id_alias;
|
||||
static ID id_scalar;
|
||||
static ID id_start_sequence;
|
||||
static ID id_end_sequence;
|
||||
static ID id_start_mapping;
|
||||
static ID id_end_mapping;
|
||||
|
||||
static int io_reader(void * data, unsigned char *buf, size_t size, size_t *read)
|
||||
{
|
||||
VALUE io = (VALUE)data;
|
||||
VALUE string = rb_funcall(io, id_read, 1, INT2NUM(size));
|
||||
|
||||
*read = 0;
|
||||
|
||||
if(! NIL_P(string)) {
|
||||
void * str = (void *)StringValuePtr(string);
|
||||
*read = (size_t)RSTRING_LEN(string);
|
||||
memcpy(buf, str, *read);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* parser.parse(yaml)
|
||||
*
|
||||
* Parse the YAML document contained in +yaml+. Events will be called on
|
||||
* the handler set on the parser instance.
|
||||
*
|
||||
* See Psych::Parser and Psych::Parser#handler
|
||||
*/
|
||||
static VALUE parse(VALUE self, VALUE yaml)
|
||||
{
|
||||
yaml_parser_t parser;
|
||||
yaml_event_t event;
|
||||
|
||||
yaml_parser_initialize(&parser);
|
||||
|
||||
if(rb_respond_to(yaml, id_read)) {
|
||||
yaml_parser_set_input(&parser, io_reader, (void *)yaml);
|
||||
} else {
|
||||
yaml_parser_set_input_string(
|
||||
&parser,
|
||||
(const unsigned char *)StringValuePtr(yaml),
|
||||
(size_t)RSTRING_LEN(yaml)
|
||||
);
|
||||
}
|
||||
|
||||
int done = 0;
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
int encoding = rb_enc_find_index("ASCII-8BIT");
|
||||
#endif
|
||||
|
||||
VALUE handler = rb_iv_get(self, "@handler");
|
||||
|
||||
while(!done) {
|
||||
if(!yaml_parser_parse(&parser, &event)) {
|
||||
size_t line = parser.mark.line;
|
||||
size_t column = parser.mark.column;
|
||||
|
||||
yaml_parser_delete(&parser);
|
||||
rb_raise(ePsychSyntaxError, "couldn't parse YAML at line %d column %d",
|
||||
(int)line, (int)column);
|
||||
}
|
||||
|
||||
switch(event.type) {
|
||||
case YAML_STREAM_START_EVENT:
|
||||
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
switch(event.data.stream_start.encoding) {
|
||||
case YAML_ANY_ENCODING:
|
||||
break;
|
||||
case YAML_UTF8_ENCODING:
|
||||
encoding = rb_enc_find_index("UTF-8");
|
||||
break;
|
||||
case YAML_UTF16LE_ENCODING:
|
||||
encoding = rb_enc_find_index("UTF-16LE");
|
||||
break;
|
||||
case YAML_UTF16BE_ENCODING:
|
||||
encoding = rb_enc_find_index("UTF-16BE");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
rb_funcall(handler, id_start_stream, 1,
|
||||
INT2NUM((long)event.data.stream_start.encoding)
|
||||
);
|
||||
break;
|
||||
case YAML_DOCUMENT_START_EVENT:
|
||||
{
|
||||
// Grab the document version
|
||||
VALUE version = event.data.document_start.version_directive ?
|
||||
rb_ary_new3(
|
||||
(long)2,
|
||||
INT2NUM((long)event.data.document_start.version_directive->major),
|
||||
INT2NUM((long)event.data.document_start.version_directive->minor)
|
||||
) : rb_ary_new();
|
||||
|
||||
// Get a list of tag directives (if any)
|
||||
VALUE tag_directives = rb_ary_new();
|
||||
if(event.data.document_start.tag_directives.start) {
|
||||
yaml_tag_directive_t *start =
|
||||
event.data.document_start.tag_directives.start;
|
||||
yaml_tag_directive_t *end =
|
||||
event.data.document_start.tag_directives.end;
|
||||
for(; start != end; start++) {
|
||||
VALUE handle = Qnil;
|
||||
if(start->handle) {
|
||||
handle = rb_str_new2((const char *)start->handle);
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
rb_enc_associate_index(handle, encoding);
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE prefix = Qnil;
|
||||
if(start->prefix) {
|
||||
prefix = rb_str_new2((const char *)start->prefix);
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
rb_enc_associate_index(prefix, encoding);
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE pair = rb_ary_new3((long)2, handle, prefix);
|
||||
rb_ary_push(tag_directives, pair);
|
||||
}
|
||||
}
|
||||
rb_funcall(handler, id_start_document, 3,
|
||||
version, tag_directives,
|
||||
event.data.document_start.implicit == 1 ? Qtrue : Qfalse
|
||||
);
|
||||
}
|
||||
break;
|
||||
case YAML_DOCUMENT_END_EVENT:
|
||||
rb_funcall(handler, id_end_document, 1,
|
||||
event.data.document_end.implicit == 1 ? Qtrue : Qfalse
|
||||
);
|
||||
break;
|
||||
case YAML_ALIAS_EVENT:
|
||||
{
|
||||
VALUE alias = Qnil;
|
||||
if(event.data.alias.anchor) {
|
||||
alias = rb_str_new2((const char *)event.data.alias.anchor);
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
rb_enc_associate_index(alias, encoding);
|
||||
#endif
|
||||
}
|
||||
|
||||
rb_funcall(handler, id_alias, 1, alias);
|
||||
}
|
||||
break;
|
||||
case YAML_SCALAR_EVENT:
|
||||
{
|
||||
VALUE val = rb_str_new(
|
||||
(const char *)event.data.scalar.value,
|
||||
(long)event.data.scalar.length
|
||||
);
|
||||
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
rb_enc_associate_index(val, encoding);
|
||||
#endif
|
||||
|
||||
VALUE anchor = Qnil;
|
||||
if(event.data.scalar.anchor) {
|
||||
anchor = rb_str_new2((const char *)event.data.scalar.anchor);
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
rb_enc_associate_index(anchor, encoding);
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE tag = Qnil;
|
||||
if(event.data.scalar.tag) {
|
||||
tag = rb_str_new2((const char *)event.data.scalar.tag);
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
rb_enc_associate_index(tag, encoding);
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE plain_implicit =
|
||||
event.data.scalar.plain_implicit == 0 ? Qfalse : Qtrue;
|
||||
|
||||
VALUE quoted_implicit =
|
||||
event.data.scalar.quoted_implicit == 0 ? Qfalse : Qtrue;
|
||||
|
||||
VALUE style = INT2NUM((long)event.data.scalar.style);
|
||||
|
||||
rb_funcall(handler, id_scalar, 6,
|
||||
val, anchor, tag, plain_implicit, quoted_implicit, style);
|
||||
}
|
||||
break;
|
||||
case YAML_SEQUENCE_START_EVENT:
|
||||
{
|
||||
VALUE anchor = Qnil;
|
||||
if(event.data.sequence_start.anchor) {
|
||||
anchor = rb_str_new2((const char *)event.data.sequence_start.anchor);
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
rb_enc_associate_index(anchor, encoding);
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE tag = Qnil;
|
||||
if(event.data.sequence_start.tag) {
|
||||
tag = rb_str_new2((const char *)event.data.sequence_start.tag);
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
rb_enc_associate_index(tag, encoding);
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE implicit =
|
||||
event.data.sequence_start.implicit == 0 ? Qfalse : Qtrue;
|
||||
|
||||
VALUE style = INT2NUM((long)event.data.sequence_start.style);
|
||||
|
||||
rb_funcall(handler, id_start_sequence, 4,
|
||||
anchor, tag, implicit, style);
|
||||
}
|
||||
break;
|
||||
case YAML_SEQUENCE_END_EVENT:
|
||||
rb_funcall(handler, id_end_sequence, 0);
|
||||
break;
|
||||
case YAML_MAPPING_START_EVENT:
|
||||
{
|
||||
VALUE anchor = Qnil;
|
||||
if(event.data.mapping_start.anchor) {
|
||||
anchor = rb_str_new2((const char *)event.data.mapping_start.anchor);
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
rb_enc_associate_index(anchor, encoding);
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE tag = Qnil;
|
||||
if(event.data.mapping_start.tag) {
|
||||
tag = rb_str_new2((const char *)event.data.mapping_start.tag);
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
rb_enc_associate_index(tag, encoding);
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE implicit =
|
||||
event.data.mapping_start.implicit == 0 ? Qfalse : Qtrue;
|
||||
|
||||
VALUE style = INT2NUM((long)event.data.mapping_start.style);
|
||||
|
||||
rb_funcall(handler, id_start_mapping, 4,
|
||||
anchor, tag, implicit, style);
|
||||
}
|
||||
break;
|
||||
case YAML_MAPPING_END_EVENT:
|
||||
rb_funcall(handler, id_end_mapping, 0);
|
||||
break;
|
||||
case YAML_NO_EVENT:
|
||||
rb_funcall(handler, id_empty, 0);
|
||||
break;
|
||||
case YAML_STREAM_END_EVENT:
|
||||
rb_funcall(handler, id_end_stream, 0);
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void Init_psych_parser()
|
||||
{
|
||||
#if 0
|
||||
mPsych = rb_define_module("Psych");
|
||||
#endif
|
||||
|
||||
cPsychParser = rb_define_class_under(mPsych, "Parser", rb_cObject);
|
||||
|
||||
/* Any encoding: Let the parser choose the encoding */
|
||||
rb_define_const(cPsychParser, "ANY", INT2NUM(YAML_ANY_ENCODING));
|
||||
|
||||
/* UTF-8 Encoding */
|
||||
rb_define_const(cPsychParser, "UTF8", INT2NUM(YAML_UTF8_ENCODING));
|
||||
|
||||
/* UTF-16-LE Encoding with BOM */
|
||||
rb_define_const(cPsychParser, "UTF16LE", INT2NUM(YAML_UTF16LE_ENCODING));
|
||||
|
||||
/* UTF-16-BE Encoding with BOM */
|
||||
rb_define_const(cPsychParser, "UTF16BE", INT2NUM(YAML_UTF16BE_ENCODING));
|
||||
|
||||
ePsychSyntaxError = rb_define_class_under(mPsych, "SyntaxError", rb_eSyntaxError);
|
||||
|
||||
rb_define_method(cPsychParser, "parse", parse, 1);
|
||||
|
||||
id_read = rb_intern("read");
|
||||
id_empty = rb_intern("empty");
|
||||
id_start_stream = rb_intern("start_stream");
|
||||
id_end_stream = rb_intern("end_stream");
|
||||
id_start_document = rb_intern("start_document");
|
||||
id_end_document = rb_intern("end_document");
|
||||
id_alias = rb_intern("alias");
|
||||
id_scalar = rb_intern("scalar");
|
||||
id_start_sequence = rb_intern("start_sequence");
|
||||
id_end_sequence = rb_intern("end_sequence");
|
||||
id_start_mapping = rb_intern("start_mapping");
|
||||
id_end_mapping = rb_intern("end_mapping");
|
||||
}
|
||||
/* vim: set noet sws=4 sw=4: */
|
6
ext/psych/parser.h
Normal file
6
ext/psych/parser.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef PSYCH_PARSER_H
|
||||
#define PSYCH_PARSER_H
|
||||
|
||||
void Init_psych_parser();
|
||||
|
||||
#endif
|
35
ext/psych/psych.c
Normal file
35
ext/psych/psych.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <psych.h>
|
||||
|
||||
/* call-seq: Psych.libyaml_version
|
||||
*
|
||||
* Returns the version of libyaml being used
|
||||
*/
|
||||
static VALUE libyaml_version(VALUE module)
|
||||
{
|
||||
int major, minor, patch;
|
||||
|
||||
yaml_get_version(&major, &minor, &patch);
|
||||
|
||||
VALUE list[3] = {
|
||||
INT2NUM((long)major),
|
||||
INT2NUM((long)minor),
|
||||
INT2NUM((long)patch)
|
||||
};
|
||||
|
||||
return rb_ary_new4((long)3, list);
|
||||
}
|
||||
|
||||
VALUE mPsych;
|
||||
|
||||
void Init_psych()
|
||||
{
|
||||
mPsych = rb_define_module("Psych");
|
||||
|
||||
rb_define_singleton_method(mPsych, "libyaml_version", libyaml_version, 0);
|
||||
|
||||
Init_psych_parser();
|
||||
Init_psych_emitter();
|
||||
Init_psych_to_ruby();
|
||||
Init_psych_yaml_tree();
|
||||
}
|
||||
/* vim: set noet sws=4 sw=4: */
|
20
ext/psych/psych.h
Normal file
20
ext/psych/psych.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef PSYCH_H
|
||||
#define PSYCH_H
|
||||
|
||||
#include <ruby.h>
|
||||
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
#include <ruby/encoding.h>
|
||||
#endif
|
||||
|
||||
#include <yaml.h>
|
||||
|
||||
#include <parser.h>
|
||||
#include <emitter.h>
|
||||
#include <to_ruby.h>
|
||||
#include <yaml_tree.h>
|
||||
|
||||
extern VALUE mPsych;
|
||||
|
||||
|
||||
#endif
|
41
ext/psych/to_ruby.c
Normal file
41
ext/psych/to_ruby.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include <psych.h>
|
||||
|
||||
VALUE cPsychVisitorsToRuby;
|
||||
|
||||
/* call-seq: vis.build_exception(klass, message)
|
||||
*
|
||||
* Create an exception with class +klass+ and +message+
|
||||
*/
|
||||
static VALUE build_exception(VALUE self, VALUE klass, VALUE mesg)
|
||||
{
|
||||
VALUE e = rb_obj_alloc(klass);
|
||||
|
||||
rb_iv_set(e, "mesg", mesg);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/* call-seq: vis.path2class(path)
|
||||
*
|
||||
* Convert +path+ string to a class
|
||||
*/
|
||||
static VALUE path2class(VALUE self, VALUE path)
|
||||
{
|
||||
#ifdef HAVE_RUBY_ENCODING_H
|
||||
return rb_path_to_class(path);
|
||||
#else
|
||||
return rb_path2class(StringValuePtr(path));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Init_psych_to_ruby(void)
|
||||
{
|
||||
VALUE psych = rb_define_module("Psych");
|
||||
VALUE visitors = rb_define_module_under(psych, "Visitors");
|
||||
VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject);
|
||||
cPsychVisitorsToRuby = rb_define_class_under(visitors, "ToRuby", visitor);
|
||||
|
||||
rb_define_private_method(cPsychVisitorsToRuby, "build_exception", build_exception, 2);
|
||||
rb_define_private_method(cPsychVisitorsToRuby, "path2class", path2class, 1);
|
||||
}
|
||||
/* vim: set noet sws=4 sw=4: */
|
8
ext/psych/to_ruby.h
Normal file
8
ext/psych/to_ruby.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef PSYCH_TO_RUBY_H
|
||||
#define PSYCH_TO_RUBY_H
|
||||
|
||||
#include <psych.h>
|
||||
|
||||
void Init_psych_to_ruby(void);
|
||||
|
||||
#endif
|
24
ext/psych/yaml_tree.c
Normal file
24
ext/psych/yaml_tree.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include <psych.h>
|
||||
|
||||
VALUE cPsychVisitorsYamlTree;
|
||||
|
||||
/*
|
||||
* call-seq: private_iv_get(target, prop)
|
||||
*
|
||||
* Get the private instance variable +prop+ from +target+
|
||||
*/
|
||||
static VALUE private_iv_get(VALUE self, VALUE target, VALUE prop)
|
||||
{
|
||||
return rb_attr_get(target, rb_intern(StringValuePtr(prop)));
|
||||
}
|
||||
|
||||
void Init_psych_yaml_tree(void)
|
||||
{
|
||||
VALUE psych = rb_define_module("Psych");
|
||||
VALUE visitors = rb_define_module_under(psych, "Visitors");
|
||||
VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject);
|
||||
cPsychVisitorsYamlTree = rb_define_class_under(visitors, "YAMLTree", visitor);
|
||||
|
||||
rb_define_private_method(cPsychVisitorsYamlTree, "private_iv_get", private_iv_get, 2);
|
||||
}
|
||||
/* vim: set noet sws=4 sw=4: */
|
8
ext/psych/yaml_tree.h
Normal file
8
ext/psych/yaml_tree.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef PSYCH_YAML_TREE_H
|
||||
#define PSYCH_YAML_TREE_H
|
||||
|
||||
#include <psych.h>
|
||||
|
||||
void Init_psych_yaml_tree(void);
|
||||
|
||||
#endif
|
236
lib/psych.rb
Normal file
236
lib/psych.rb
Normal file
|
@ -0,0 +1,236 @@
|
|||
require 'psych/psych'
|
||||
require 'psych/nodes'
|
||||
require 'psych/visitors'
|
||||
require 'psych/handler'
|
||||
require 'psych/tree_builder'
|
||||
require 'psych/parser'
|
||||
require 'psych/omap'
|
||||
require 'psych/set'
|
||||
require 'psych/coder'
|
||||
require 'psych/core_ext'
|
||||
|
||||
###
|
||||
# = Overview
|
||||
#
|
||||
# Psych is a YAML parser and emitter. Psych leverages
|
||||
# libyaml[http://libyaml.org] for it's YAML parsing and emitting capabilities.
|
||||
# In addition to wrapping libyaml, Psych also knows how to serialize and
|
||||
# de-serialize most Ruby objects to and from the YAML format.
|
||||
#
|
||||
# = I NEED TO PARSE OR EMIT YAML RIGHT NOW!
|
||||
#
|
||||
# # Parse some YAML
|
||||
# Psych.load("--- foo") # => "foo"
|
||||
#
|
||||
# # Emit some YAML
|
||||
# Psych.dump("foo") # => "--- foo\n...\n"
|
||||
# { :a => 'b'}.to_yaml # => "---\n:a: b\n"
|
||||
#
|
||||
# Got more time on your hands? Keep on reading!
|
||||
#
|
||||
# == YAML Parsing
|
||||
#
|
||||
# Psych provides a range of interfaces for parsing a YAML document ranging from
|
||||
# low level to high level, depending on your parsing needs. At the lowest
|
||||
# level, is an event based parser. Mid level is access to the raw YAML AST,
|
||||
# and at the highest level is the ability to unmarshal YAML to ruby objects.
|
||||
#
|
||||
# === Low level parsing
|
||||
#
|
||||
# The lowest level parser should be used when the YAML input is already known,
|
||||
# and the developer does not want to pay the price of building an AST or
|
||||
# automatic detection and conversion to ruby objects. See Psych::Parser for
|
||||
# more information on using the event based parser.
|
||||
#
|
||||
# === Mid level parsing
|
||||
#
|
||||
# Psych provides access to an AST produced from parsing a YAML document. This
|
||||
# tree is built using the Psych::Parser and Psych::TreeBuilder. The AST can
|
||||
# be examined and manipulated freely. Please see Psych::parse_stream,
|
||||
# Psych::Nodes, and Psych::Nodes::Node for more information on dealing with
|
||||
# YAML syntax trees.
|
||||
#
|
||||
# === High level parsing
|
||||
#
|
||||
# The high level YAML parser provided by Psych simply takes YAML as input and
|
||||
# returns a Ruby data structure. For information on using the high level parser
|
||||
# see Psych.load
|
||||
#
|
||||
# == YAML Emitting
|
||||
#
|
||||
# Psych provides a range of interfaces ranging from low to high level for
|
||||
# producing YAML documents. Very similar to the YAML parsing interfaces, Psych
|
||||
# provides at the lowest level, an event based system, mid-level is building
|
||||
# a YAML AST, and the highest level is converting a Ruby object straight to
|
||||
# a YAML document.
|
||||
#
|
||||
# === Low level emitting
|
||||
#
|
||||
# The lowest level emitter is an event based system. Events are sent to a
|
||||
# Psych::Emitter object. That object knows how to convert the events to a YAML
|
||||
# document. This interface should be used when document format is known in
|
||||
# advance or speed is a concern. See Psych::Emitter for more information.
|
||||
#
|
||||
# === Mid level emitting
|
||||
#
|
||||
# At the mid level is building an AST. This AST is exactly the same as the AST
|
||||
# used when parsing a YAML document. Users can build an AST by hand and the
|
||||
# AST knows how to emit itself as a YAML document. See Psych::Nodes,
|
||||
# Psych::Nodes::Node, and Psych::TreeBuilder for more information on building
|
||||
# a YAML AST.
|
||||
#
|
||||
# === High level emitting
|
||||
#
|
||||
# The high level emitter has the easiest interface. Psych simply takes a Ruby
|
||||
# data structure and converts it to a YAML document. See Psych.dump for more
|
||||
# information on dumping a Ruby data structure.
|
||||
|
||||
module Psych
|
||||
# The version is Psych you're using
|
||||
VERSION = '1.0.0'
|
||||
|
||||
# The version of libyaml Psych is using
|
||||
LIBYAML_VERSION = Psych.libyaml_version.join '.'
|
||||
|
||||
###
|
||||
# Load +yaml+ in to a Ruby data structure. If multiple documents are
|
||||
# provided, the object contained in the first document will be returned.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Psych.load("--- a") # => 'a'
|
||||
# Psych.load("---\n - a\n - b") # => ['a', 'b']
|
||||
def self.load yaml
|
||||
parse(yaml).to_ruby
|
||||
end
|
||||
|
||||
###
|
||||
# Parse a YAML string in +yaml+. Returns the first object of a YAML AST.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Psych.load("---\n - a\n - b") # => #<Psych::Nodes::Sequence:0x00>
|
||||
#
|
||||
# See Psych::Nodes for more information about YAML AST.
|
||||
def self.parse yaml
|
||||
parse_stream(yaml).children.first.children.first
|
||||
end
|
||||
|
||||
###
|
||||
# Parse a file at +filename+. Returns the YAML AST.
|
||||
def self.parse_file filename
|
||||
File.open filename do |f|
|
||||
parse f
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
# Returns a default parser
|
||||
def self.parser
|
||||
Psych::Parser.new(TreeBuilder.new)
|
||||
end
|
||||
|
||||
###
|
||||
# Parse a YAML string in +yaml+. Returns the full AST for the YAML document.
|
||||
# This method can handle multiple YAML documents contained in +yaml+.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Psych.load("---\n - a\n - b") # => #<Psych::Nodes::Stream:0x00>
|
||||
#
|
||||
# See Psych::Nodes for more information about YAML AST.
|
||||
def self.parse_stream yaml
|
||||
parser = self.parser
|
||||
parser.parse yaml
|
||||
parser.handler.root
|
||||
end
|
||||
|
||||
###
|
||||
# Dump Ruby object +o+ to a YAML string using +options+.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Psych.dump(['a', 'b']) # => "---\n- a\n- b\n"
|
||||
def self.dump o, options = {}
|
||||
visitor = Psych::Visitors::YAMLTree.new options
|
||||
visitor << o
|
||||
visitor.tree.to_yaml
|
||||
end
|
||||
|
||||
###
|
||||
# Dump a list of objects as separate documents to a document stream.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Psych.dump_stream("foo\n ", {}) # => "--- ! \"foo\\n \"\n--- {}\n"
|
||||
def self.dump_stream *objects
|
||||
visitor = Psych::Visitors::YAMLTree.new {}
|
||||
objects.each do |o|
|
||||
visitor << o
|
||||
end
|
||||
visitor.tree.to_yaml
|
||||
end
|
||||
|
||||
###
|
||||
# Dump Ruby object +o+ to a JSON string.
|
||||
def self.to_json o
|
||||
visitor = Psych::Visitors::JSONTree.new(:json => true)
|
||||
visitor << o
|
||||
visitor.tree.to_yaml
|
||||
end
|
||||
|
||||
###
|
||||
# Load multiple documents given in +yaml+. Returns the parsed documents
|
||||
# as a list. For example:
|
||||
#
|
||||
# Psych.load_documents("--- foo\n...\n--- bar\n...") # => ['foo', 'bar']
|
||||
#
|
||||
def self.load_stream yaml
|
||||
parse_stream(yaml).children.map { |child| child.to_ruby }
|
||||
end
|
||||
|
||||
def self.load_documents yaml, &block
|
||||
if $VERBOSE
|
||||
warn "#{caller[0]}: load_documents is deprecated, use load_stream"
|
||||
end
|
||||
list = load_stream yaml
|
||||
return list unless block_given?
|
||||
list.each(&block)
|
||||
end
|
||||
|
||||
###
|
||||
# Load the document contained in +filename+. Returns the yaml contained in
|
||||
# +filename+ as a ruby object
|
||||
def self.load_file filename
|
||||
self.load File.open(filename)
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
@domain_types = {}
|
||||
def self.add_domain_type domain, type_tag, &block
|
||||
@domain_types[type_tag] = ["http://#{domain}", block]
|
||||
end
|
||||
|
||||
def self.add_builtin_type type_tag, &block
|
||||
@domain_types[type_tag] = ['yaml.org', block]
|
||||
end
|
||||
|
||||
def self.remove_type type_tag
|
||||
@domain_types.delete type_tag
|
||||
end
|
||||
|
||||
@load_tags = {}
|
||||
@dump_tags = {}
|
||||
def self.add_tag tag, klass
|
||||
@load_tags[tag] = klass
|
||||
@dump_tags[klass] = tag
|
||||
end
|
||||
|
||||
class << self
|
||||
attr_accessor :load_tags
|
||||
attr_accessor :dump_tags
|
||||
attr_accessor :domain_types
|
||||
end
|
||||
|
||||
# :startdoc:
|
||||
end
|
68
lib/psych/coder.rb
Normal file
68
lib/psych/coder.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
module Psych
|
||||
###
|
||||
# If an object defines +encode_with+, then an instance of Psych::Coder will
|
||||
# passed to the method when the object is being serialized. The Coder
|
||||
# automatically assumes a Psych::Nodes::Mapping is being emitted. Other
|
||||
# objects like Sequence and Scalar may be emitted if +seq=+ or +scalar=+ are
|
||||
# called, respectively.
|
||||
class Coder
|
||||
attr_accessor :tag, :style, :implicit
|
||||
attr_reader :type, :map, :scalar, :seq
|
||||
|
||||
def initialize tag
|
||||
@map = {}
|
||||
@seq = []
|
||||
@implicit = false
|
||||
@type = :map
|
||||
@tag = tag
|
||||
@style = Psych::Nodes::Mapping::BLOCK
|
||||
@scalar = nil
|
||||
end
|
||||
|
||||
# Emit a scalar with +value+ and +tag+
|
||||
def represent_scalar tag, value
|
||||
self.tag = tag
|
||||
self.scalar = value
|
||||
end
|
||||
|
||||
# Emit a sequence with +list+ and +tag+
|
||||
def represent_seq tag, list
|
||||
@tag = tag
|
||||
self.seq = list
|
||||
end
|
||||
|
||||
# Emit a sequence with +map+ and +tag+
|
||||
def represent_map tag, map
|
||||
@tag = tag
|
||||
self.map = map
|
||||
end
|
||||
|
||||
# Emit a scalar with +value+
|
||||
def scalar= value
|
||||
@type = :scalar
|
||||
@scalar = value
|
||||
end
|
||||
|
||||
# Emit a map with +value+
|
||||
def map= map
|
||||
@type = :map
|
||||
@map = map
|
||||
end
|
||||
|
||||
def []= k, v
|
||||
@type = :map
|
||||
@map[k] = v
|
||||
end
|
||||
|
||||
def [] k
|
||||
@type = :map
|
||||
@map[k]
|
||||
end
|
||||
|
||||
# Emit a sequence of +list+
|
||||
def seq= list
|
||||
@type = :seq
|
||||
@seq = list
|
||||
end
|
||||
end
|
||||
end
|
22
lib/psych/core_ext.rb
Normal file
22
lib/psych/core_ext.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
class Object
|
||||
def self.yaml_tag url
|
||||
Psych.add_tag(url, self)
|
||||
end
|
||||
|
||||
# FIXME: rename this to "to_yaml" when syck is removed
|
||||
|
||||
###
|
||||
# call-seq: to_yaml
|
||||
#
|
||||
# Convert an object to YAML
|
||||
def psych_to_yaml options = {}
|
||||
Psych.dump self, options
|
||||
end
|
||||
alias :to_yaml :psych_to_yaml
|
||||
end
|
||||
|
||||
module Kernel
|
||||
def y *objects
|
||||
puts Psych.dump_stream(*objects)
|
||||
end
|
||||
end
|
4
lib/psych/emitter.rb
Normal file
4
lib/psych/emitter.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
module Psych
|
||||
class Emitter < Psych::Handler
|
||||
end
|
||||
end
|
215
lib/psych/handler.rb
Normal file
215
lib/psych/handler.rb
Normal file
|
@ -0,0 +1,215 @@
|
|||
module Psych
|
||||
###
|
||||
# Psych::Handler is an abstract base class that defines the events used
|
||||
# when dealing with Psych::Parser. Clients who want to use Psych::Parser
|
||||
# should implement a class that inherits from Psych::Handler and define
|
||||
# events that they can handle.
|
||||
#
|
||||
# Psych::Handler defines all events that Psych::Parser can possibly send to
|
||||
# event handlers.
|
||||
#
|
||||
# See Psych::Parser for more details
|
||||
class Handler
|
||||
###
|
||||
# Called with +encoding+ when the YAML stream starts. This method is
|
||||
# called once per stream. A stream may contain multiple documents.
|
||||
#
|
||||
# See the constants in Psych::Parser for the possible values of +encoding+.
|
||||
def start_stream encoding
|
||||
end
|
||||
|
||||
###
|
||||
# Called when the document starts with the declared +version+,
|
||||
# +tag_directives+, if the document is +implicit+.
|
||||
#
|
||||
# +version+ will be an array of integers indicating the YAML version being
|
||||
# dealt with, +tag_directives+ is a list of tuples indicating the prefix
|
||||
# and suffix of each tag, and +implicit+ is a boolean indicating whether
|
||||
# the document is started implicitly.
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# Given the following YAML:
|
||||
#
|
||||
# %YAML 1.1
|
||||
# %TAG ! tag:tenderlovemaking.com,2009:
|
||||
# --- !squee
|
||||
#
|
||||
# The parameters for start_document must be this:
|
||||
#
|
||||
# version # => [1, 1]
|
||||
# tag_directives # => [["!", "tag:tenderlovemaking.com,2009:"]]
|
||||
# implicit # => false
|
||||
def start_document version, tag_directives, implicit
|
||||
end
|
||||
|
||||
###
|
||||
# Called with the document ends. +implicit+ is a boolean value indicating
|
||||
# whether or not the document has an implicit ending.
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# Given the following YAML:
|
||||
#
|
||||
# ---
|
||||
# hello world
|
||||
#
|
||||
# +implicit+ will be true. Given this YAML:
|
||||
#
|
||||
# ---
|
||||
# hello world
|
||||
# ...
|
||||
#
|
||||
# +implicit+ will be false.
|
||||
def end_document implicit
|
||||
end
|
||||
|
||||
###
|
||||
# Called when an alias is found to +anchor+. +anchor+ will be the name
|
||||
# of the anchor found.
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# Here we have an example of an array that references itself in YAML:
|
||||
#
|
||||
# --- &ponies
|
||||
# - first element
|
||||
# - *ponies
|
||||
#
|
||||
# &ponies is the achor, *ponies is the alias. In this case, alias is
|
||||
# called with "ponies".
|
||||
def alias anchor
|
||||
end
|
||||
|
||||
###
|
||||
# Called when a scalar +value+ is found. The scalar may have an
|
||||
# +anchor+, a +tag+, be implicitly +plain+ or implicitly +quoted+
|
||||
#
|
||||
# +value+ is the string value of the scalar
|
||||
# +anchor+ is an associated anchor or nil
|
||||
# +tag+ is an associated tag or nil
|
||||
# +plain+ is a boolean value
|
||||
# +quoted+ is a boolean value
|
||||
# +style+ is an integer idicating the string style
|
||||
#
|
||||
# See the constants in Psych::Nodes::Scalar for the possible values of
|
||||
# +style+
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# Here is a YAML document that exercises most of the possible ways this
|
||||
# method can be called:
|
||||
#
|
||||
# ---
|
||||
# - !str "foo"
|
||||
# - &anchor fun
|
||||
# - many
|
||||
# lines
|
||||
# - |
|
||||
# many
|
||||
# newlines
|
||||
#
|
||||
# The above YAML document contains a list with four strings. Here are
|
||||
# the parameters sent to this method in the same order:
|
||||
#
|
||||
# # value anchor tag plain quoted style
|
||||
# ["foo", nil, "!str", false, false, 3 ]
|
||||
# ["fun", "anchor", nil, true, false, 1 ]
|
||||
# ["many lines", nil, nil, true, false, 1 ]
|
||||
# ["many\nnewlines\n", nil, nil, false, true, 4 ]
|
||||
#
|
||||
def scalar value, anchor, tag, plain, quoted, style
|
||||
end
|
||||
|
||||
###
|
||||
# Called when a sequence is started.
|
||||
#
|
||||
# +anchor+ is the anchor associated with the sequence or nil.
|
||||
# +tag+ is the tag associated with the sequence or nil.
|
||||
# +implicit+ a boolean indicating whether or not the sequence was implicitly
|
||||
# started.
|
||||
# +style+ is an integer indicating the list style.
|
||||
#
|
||||
# See the constants in Psych::Nodes::Sequence for the possible values of
|
||||
# +style+.
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# Here is a YAML document that exercises most of the possible ways this
|
||||
# method can be called:
|
||||
#
|
||||
# ---
|
||||
# - !!seq [
|
||||
# a
|
||||
# ]
|
||||
# - &pewpew
|
||||
# - b
|
||||
#
|
||||
# The above YAML document consists of three lists, an outer list that
|
||||
# contains two inner lists. Here is a matrix of the parameters sent
|
||||
# to represent these lists:
|
||||
#
|
||||
# # anchor tag implicit style
|
||||
# [nil, nil, true, 1 ]
|
||||
# [nil, "tag:yaml.org,2002:seq", false, 2 ]
|
||||
# ["pewpew", nil, true, 1 ]
|
||||
|
||||
def start_sequence anchor, tag, implicit, style
|
||||
end
|
||||
|
||||
###
|
||||
# Called when a sequence ends.
|
||||
def end_sequence
|
||||
end
|
||||
|
||||
###
|
||||
# Called when a map starts.
|
||||
#
|
||||
# +anchor+ is the anchor associated with the map or +nil+.
|
||||
# +tag+ is the tag associated with the map or +nil+.
|
||||
# +implicit+ is a boolean indicating whether or not the map was implicitly
|
||||
# started.
|
||||
# +style+ is an integer indicating the mapping style.
|
||||
#
|
||||
# See the constants in Psych::Nodes::Mapping for the possible values of
|
||||
# +style+.
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# Here is a YAML document that exercises most of the possible ways this
|
||||
# method can be called:
|
||||
#
|
||||
# ---
|
||||
# k: !!map { hello: world }
|
||||
# v: &pewpew
|
||||
# hello: world
|
||||
#
|
||||
# The above YAML document consists of three maps, an outer map that contains
|
||||
# two inner maps. Below is a matrix of the parameters sent in order to
|
||||
# represent these three maps:
|
||||
#
|
||||
# # anchor tag implicit style
|
||||
# [nil, nil, true, 1 ]
|
||||
# [nil, "tag:yaml.org,2002:map", false, 2 ]
|
||||
# ["pewpew", nil, true, 1 ]
|
||||
|
||||
def start_mapping anchor, tag, implicit, style
|
||||
end
|
||||
|
||||
###
|
||||
# Called when a map ends
|
||||
def end_mapping
|
||||
end
|
||||
|
||||
###
|
||||
# Called when an empty event happens. (Which, as far as I can tell, is
|
||||
# never).
|
||||
def empty
|
||||
end
|
||||
|
||||
###
|
||||
# Called when the YAML stream ends
|
||||
def end_stream
|
||||
end
|
||||
end
|
||||
end
|
77
lib/psych/nodes.rb
Normal file
77
lib/psych/nodes.rb
Normal file
|
@ -0,0 +1,77 @@
|
|||
require 'psych/nodes/node'
|
||||
require 'psych/nodes/stream'
|
||||
require 'psych/nodes/document'
|
||||
require 'psych/nodes/sequence'
|
||||
require 'psych/nodes/scalar'
|
||||
require 'psych/nodes/mapping'
|
||||
require 'psych/nodes/alias'
|
||||
|
||||
module Psych
|
||||
###
|
||||
# = Overview
|
||||
#
|
||||
# When using Psych.load to deserialize a YAML document, the document is
|
||||
# translated to an intermediary AST. That intermediary AST is then
|
||||
# translated in to a Ruby object graph.
|
||||
#
|
||||
# In the opposite direction, when using Psych.dump, the Ruby object graph is
|
||||
# translated to an intermediary AST which is then converted to a YAML
|
||||
# document.
|
||||
#
|
||||
# Psych::Nodes contains all of the classes that make up the nodes of a YAML
|
||||
# AST. You can manually build an AST and use one of the visitors (see
|
||||
# Psych::Visitors) to convert that AST to either a YAML document or to a
|
||||
# Ruby object graph.
|
||||
#
|
||||
# Here is an example of building an AST that represents a list with one
|
||||
# scalar:
|
||||
#
|
||||
# # Create our nodes
|
||||
# stream = Psych::Nodes::Stream.new
|
||||
# doc = Psych::Nodes::Document.new
|
||||
# seq = Psych::Nodes::Sequence.new
|
||||
# scalar = Psych::Nodes::Scalar.new('foo')
|
||||
#
|
||||
# # Build up our tree
|
||||
# stream.children << doc
|
||||
# doc.children << seq
|
||||
# seq.children << scalar
|
||||
#
|
||||
# The stream is the root of the tree. We can then convert the tree to YAML:
|
||||
#
|
||||
# stream.to_yaml => "---\n- foo\n"
|
||||
#
|
||||
# Or convert it to Ruby:
|
||||
#
|
||||
# stream.to_ruby => [["foo"]]
|
||||
#
|
||||
# == YAML AST Requirements
|
||||
#
|
||||
# A valid YAML AST *must* have one Psych::Nodes::Stream at the root. A
|
||||
# Psych::Nodes::Stream node must have 1 or more Psych::Nodes::Document nodes
|
||||
# as children.
|
||||
#
|
||||
# Psych::Nodes::Document nodes must have one and *only* one child. That child
|
||||
# may be one of:
|
||||
#
|
||||
# * Psych::Nodes::Sequence
|
||||
# * Psych::Nodes::Mapping
|
||||
# * Psych::Nodes::Scalar
|
||||
#
|
||||
# Psych::Nodes::Sequence and Psych::Nodes::Mapping nodes may have many
|
||||
# children, but Psych::Nodes::Mapping nodes should have an even number of
|
||||
# children.
|
||||
#
|
||||
# All of these are valid children for Psych::Nodes::Sequence and
|
||||
# Psych::Nodes::Mapping nodes:
|
||||
#
|
||||
# * Psych::Nodes::Sequence
|
||||
# * Psych::Nodes::Mapping
|
||||
# * Psych::Nodes::Scalar
|
||||
# * Psych::Nodes::Alias
|
||||
#
|
||||
# Psych::Nodes::Scalar and Psych::Nodes::Alias are both terminal nodes and
|
||||
# should not have any children.
|
||||
module Nodes
|
||||
end
|
||||
end
|
18
lib/psych/nodes/alias.rb
Normal file
18
lib/psych/nodes/alias.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
module Psych
|
||||
module Nodes
|
||||
###
|
||||
# This class represents a {YAML Alias}[http://yaml.org/spec/1.1/#alias].
|
||||
# It points to an +anchor+.
|
||||
#
|
||||
# A Psych::Nodes::Alias is a terminal node and may have no children.
|
||||
class Alias < Psych::Nodes::Node
|
||||
# The anchor this alias links to
|
||||
attr_accessor :anchor
|
||||
|
||||
# Create a new Alias that points to an +anchor+
|
||||
def initialize anchor
|
||||
@anchor = anchor
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
60
lib/psych/nodes/document.rb
Normal file
60
lib/psych/nodes/document.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
module Psych
|
||||
module Nodes
|
||||
###
|
||||
# This represents a YAML Document. This node must be a child of
|
||||
# Psych::Nodes::Stream. A Psych::Nodes::Document must have one child,
|
||||
# and that child may be one of the following:
|
||||
#
|
||||
# * Psych::Nodes::Sequence
|
||||
# * Psych::Nodes::Mapping
|
||||
# * Psych::Nodes::Scalar
|
||||
class Document < Psych::Nodes::Node
|
||||
# The version of the YAML document
|
||||
attr_accessor :version
|
||||
|
||||
# A list of tag directives for this document
|
||||
attr_accessor :tag_directives
|
||||
|
||||
# Was this document implicitly created?
|
||||
attr_accessor :implicit
|
||||
|
||||
# Is the end of the document implicit?
|
||||
attr_accessor :implicit_end
|
||||
|
||||
###
|
||||
# Create a new Psych::Nodes::Document object.
|
||||
#
|
||||
# +version+ is a list indicating the YAML version.
|
||||
# +tags_directives+ is a list of tag directive declarations
|
||||
# +implicit+ is a flag indicating whether the document will be implicitly
|
||||
# started.
|
||||
#
|
||||
# == Example:
|
||||
# This creates a YAML document object that represents a YAML 1.1 document
|
||||
# with one tag directive, and has an implicit start:
|
||||
#
|
||||
# Psych::Nodes::Document.new(
|
||||
# [1,1],
|
||||
# [["!", "tag:tenderlovemaking.com,2009:"]],
|
||||
# true
|
||||
# )
|
||||
#
|
||||
# == See Also
|
||||
# See also Psych::Handler#start_document
|
||||
def initialize version = [], tag_directives = [], implicit = false
|
||||
super()
|
||||
@version = version
|
||||
@tag_directives = tag_directives
|
||||
@implicit = implicit
|
||||
@implicit_end = true
|
||||
end
|
||||
|
||||
###
|
||||
# Returns the root node. A Document may only have one root node:
|
||||
# http://yaml.org/spec/1.1/#id898031
|
||||
def root
|
||||
children.first
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
56
lib/psych/nodes/mapping.rb
Normal file
56
lib/psych/nodes/mapping.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
module Psych
|
||||
module Nodes
|
||||
###
|
||||
# This class represents a {YAML Mapping}[http://yaml.org/spec/1.1/#mapping].
|
||||
#
|
||||
# A Psych::Nodes::Mapping node may have 0 or more children, but must have
|
||||
# an even number of children. Here are the valid children a
|
||||
# Psych::Nodes::Mapping node may have:
|
||||
#
|
||||
# * Psych::Nodes::Sequence
|
||||
# * Psych::Nodes::Mapping
|
||||
# * Psych::Nodes::Scalar
|
||||
# * Psych::Nodes::Alias
|
||||
class Mapping < Psych::Nodes::Node
|
||||
# Any Map Style
|
||||
ANY = 0
|
||||
|
||||
# Block Map Style
|
||||
BLOCK = 1
|
||||
|
||||
# Flow Map Style
|
||||
FLOW = 2
|
||||
|
||||
# The optional anchor for this mapping
|
||||
attr_accessor :anchor
|
||||
|
||||
# The optional tag for this mapping
|
||||
attr_accessor :tag
|
||||
|
||||
# Is this an implicit mapping?
|
||||
attr_accessor :implicit
|
||||
|
||||
# The style of this mapping
|
||||
attr_accessor :style
|
||||
|
||||
###
|
||||
# Create a new Psych::Nodes::Mapping object.
|
||||
#
|
||||
# +anchor+ is the anchor associated with the map or +nil+.
|
||||
# +tag+ is the tag associated with the map or +nil+.
|
||||
# +implicit+ is a boolean indicating whether or not the map was implicitly
|
||||
# started.
|
||||
# +style+ is an integer indicating the mapping style.
|
||||
#
|
||||
# == See Also
|
||||
# See also Psych::Handler#start_mapping
|
||||
def initialize anchor = nil, tag = nil, implicit = true, style = BLOCK
|
||||
super()
|
||||
@anchor = anchor
|
||||
@tag = tag
|
||||
@implicit = implicit
|
||||
@style = style
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
40
lib/psych/nodes/node.rb
Normal file
40
lib/psych/nodes/node.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
require 'stringio'
|
||||
|
||||
module Psych
|
||||
module Nodes
|
||||
###
|
||||
# The base class for any Node in a YAML parse tree. This class should
|
||||
# never be instantiated.
|
||||
class Node
|
||||
# The children of this node
|
||||
attr_reader :children
|
||||
|
||||
# An associated tag
|
||||
attr_reader :tag
|
||||
|
||||
# Create a new Psych::Nodes::Node
|
||||
def initialize
|
||||
@children = []
|
||||
end
|
||||
|
||||
###
|
||||
# Convert this node to Ruby.
|
||||
#
|
||||
# See also Psych::Visitors::ToRuby
|
||||
def to_ruby
|
||||
Visitors::ToRuby.new.accept self
|
||||
end
|
||||
alias :transform :to_ruby
|
||||
|
||||
###
|
||||
# Convert this node to YAML.
|
||||
#
|
||||
# See also Psych::Visitors::Emitter
|
||||
def to_yaml
|
||||
io = StringIO.new
|
||||
Visitors::Emitter.new(io).accept self
|
||||
io.string
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
67
lib/psych/nodes/scalar.rb
Normal file
67
lib/psych/nodes/scalar.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
module Psych
|
||||
module Nodes
|
||||
###
|
||||
# This class represents a {YAML Scalar}[http://yaml.org/spec/1.1/#id858081].
|
||||
#
|
||||
# This node type is a terminal node and should not have any children.
|
||||
class Scalar < Psych::Nodes::Node
|
||||
# Any style scalar, the emitter chooses
|
||||
ANY = 0
|
||||
|
||||
# Plain scalar style
|
||||
PLAIN = 1
|
||||
|
||||
# Single quoted style
|
||||
SINGLE_QUOTED = 2
|
||||
|
||||
# Double quoted style
|
||||
DOUBLE_QUOTED = 3
|
||||
|
||||
# Literal style
|
||||
LITERAL = 4
|
||||
|
||||
# Folded style
|
||||
FOLDED = 5
|
||||
|
||||
# The scalar value
|
||||
attr_accessor :value
|
||||
|
||||
# The anchor value (if there is one)
|
||||
attr_accessor :anchor
|
||||
|
||||
# The tag value (if there is one)
|
||||
attr_accessor :tag
|
||||
|
||||
# Is this a plain scalar?
|
||||
attr_accessor :plain
|
||||
|
||||
# Is this scalar quoted?
|
||||
attr_accessor :quoted
|
||||
|
||||
# The style of this scalar
|
||||
attr_accessor :style
|
||||
|
||||
###
|
||||
# Create a new Psych::Nodes::Scalar object.
|
||||
#
|
||||
# +value+ is the string value of the scalar
|
||||
# +anchor+ is an associated anchor or nil
|
||||
# +tag+ is an associated tag or nil
|
||||
# +plain+ is a boolean value
|
||||
# +quoted+ is a boolean value
|
||||
# +style+ is an integer idicating the string style
|
||||
#
|
||||
# == See Also
|
||||
#
|
||||
# See also Psych::Handler#scalar
|
||||
def initialize value, anchor = nil, tag = nil, plain = true, quoted = false, style = ANY
|
||||
@value = value
|
||||
@anchor = anchor
|
||||
@tag = tag
|
||||
@plain = plain
|
||||
@quoted = quoted
|
||||
@style = style
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
81
lib/psych/nodes/sequence.rb
Normal file
81
lib/psych/nodes/sequence.rb
Normal file
|
@ -0,0 +1,81 @@
|
|||
module Psych
|
||||
module Nodes
|
||||
###
|
||||
# This class represents a
|
||||
# {YAML sequence}[http://yaml.org/spec/1.1/#sequence/syntax].
|
||||
#
|
||||
# A YAML sequence is basically a list, and looks like this:
|
||||
#
|
||||
# %YAML 1.1
|
||||
# ---
|
||||
# - I am
|
||||
# - a Sequence
|
||||
#
|
||||
# A YAML sequence may have an anchor like this:
|
||||
#
|
||||
# %YAML 1.1
|
||||
# ---
|
||||
# &A [
|
||||
# "This sequence",
|
||||
# "has an anchor"
|
||||
# ]
|
||||
#
|
||||
# A YAML sequence may also have a tag like this:
|
||||
#
|
||||
# %YAML 1.1
|
||||
# ---
|
||||
# !!seq [
|
||||
# "This sequence",
|
||||
# "has a tag"
|
||||
# ]
|
||||
#
|
||||
# This class represents a sequence in a YAML document. A
|
||||
# Psych::Nodes::Sequence node may have 0 or more children. Valid children
|
||||
# for this node are:
|
||||
#
|
||||
# * Psych::Nodes::Sequence
|
||||
# * Psych::Nodes::Mapping
|
||||
# * Psych::Nodes::Scalar
|
||||
# * Psych::Nodes::Alias
|
||||
class Sequence < Psych::Nodes::Node
|
||||
# Any Styles, emitter chooses
|
||||
ANY = 0
|
||||
|
||||
# Block style sequence
|
||||
BLOCK = 1
|
||||
|
||||
# Flow style sequence
|
||||
FLOW = 2
|
||||
|
||||
# The anchor for this sequence (if any)
|
||||
attr_accessor :anchor
|
||||
|
||||
# The tag name for this sequence (if any)
|
||||
attr_accessor :tag
|
||||
|
||||
# Is this sequence started implicitly?
|
||||
attr_accessor :implicit
|
||||
|
||||
# The sequece style used
|
||||
attr_accessor :style
|
||||
|
||||
###
|
||||
# Create a new object representing a YAML sequence.
|
||||
#
|
||||
# +anchor+ is the anchor associated with the sequence or nil.
|
||||
# +tag+ is the tag associated with the sequence or nil.
|
||||
# +implicit+ a boolean indicating whether or not the sequence was
|
||||
# implicitly started.
|
||||
# +style+ is an integer indicating the list style.
|
||||
#
|
||||
# See Psych::Handler#start_sequence
|
||||
def initialize anchor = nil, tag = nil, implicit = true, style = BLOCK
|
||||
super()
|
||||
@anchor = anchor
|
||||
@tag = tag
|
||||
@implicit = implicit
|
||||
@style = style
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
37
lib/psych/nodes/stream.rb
Normal file
37
lib/psych/nodes/stream.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
module Psych
|
||||
module Nodes
|
||||
###
|
||||
# Represents a YAML stream. This is the root node for any YAML parse
|
||||
# tree. This node must have one or more child nodes. The only valid
|
||||
# child node for a Psych::Nodes::Stream node is Psych::Nodes::Document.
|
||||
class Stream < Psych::Nodes::Node
|
||||
|
||||
# Encodings supported by Psych (and libyaml)
|
||||
|
||||
# Any encoding
|
||||
ANY = Psych::Parser::ANY
|
||||
|
||||
# UTF-8 encoding
|
||||
UTF8 = Psych::Parser::UTF8
|
||||
|
||||
# UTF-16LE encoding
|
||||
UTF16LE = Psych::Parser::UTF16LE
|
||||
|
||||
# UTF-16BE encoding
|
||||
UTF16BE = Psych::Parser::UTF16BE
|
||||
|
||||
# The encoding used for this stream
|
||||
attr_reader :encoding
|
||||
|
||||
###
|
||||
# Create a new Psych::Nodes::Stream node with an +encoding+ that
|
||||
# defaults to Psych::Nodes::Stream::UTF8.
|
||||
#
|
||||
# See also Psych::Handler#start_stream
|
||||
def initialize encoding = UTF8
|
||||
super()
|
||||
@encoding = encoding
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
4
lib/psych/omap.rb
Normal file
4
lib/psych/omap.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
module Psych
|
||||
class Omap < ::Hash
|
||||
end
|
||||
end
|
44
lib/psych/parser.rb
Normal file
44
lib/psych/parser.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
module Psych
|
||||
###
|
||||
# YAML event parser class. This class parses a YAML document and calls
|
||||
# events on the handler that is passed to the constructor. The events can
|
||||
# be used for things such as constructing a YAML AST or deserializing YAML
|
||||
# documents. It can even be fed back to Psych::Emitter to emit the same
|
||||
# document that was parsed.
|
||||
#
|
||||
# See Psych::Handler for documentation on the events that Psych::Parser emits.
|
||||
#
|
||||
# Here is an example that prints out ever scalar found in a YAML document:
|
||||
#
|
||||
# # Handler for detecting scalar values
|
||||
# class ScalarHandler < Psych::Handler
|
||||
# def scalar value, anchor, tag, plain, quoted, style
|
||||
# puts value
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# parser = Psych::Parser.new(ScalarHandler.new)
|
||||
# parser.parse(yaml_document)
|
||||
#
|
||||
# Here is an example that feeds the parser back in to Psych::Emitter. The
|
||||
# YAML document is read from STDIN and written back out to STDERR:
|
||||
#
|
||||
# parser = Psych::Parser.new(Psych::Emitter.new($stderr))
|
||||
# parser.parse($stdin)
|
||||
#
|
||||
# Psych uses Psych::Parser in combination with Psych::TreeBuilder to
|
||||
# construct an AST of the parsed YAML document.
|
||||
|
||||
class Parser
|
||||
# The handler on which events will be called
|
||||
attr_accessor :handler
|
||||
|
||||
###
|
||||
# Creates a new Psych::Parser instance with +handler+. YAML events will
|
||||
# be called on +handler+. See Psych::Parser for more details.
|
||||
|
||||
def initialize handler = Handler.new
|
||||
@handler = handler
|
||||
end
|
||||
end
|
||||
end
|
90
lib/psych/scalar_scanner.rb
Normal file
90
lib/psych/scalar_scanner.rb
Normal file
|
@ -0,0 +1,90 @@
|
|||
require 'strscan'
|
||||
|
||||
module Psych
|
||||
###
|
||||
# Scan scalars for built in types
|
||||
class ScalarScanner
|
||||
# Taken from http://yaml.org/type/timestamp.html
|
||||
TIME = /^\d{4}-\d{1,2}-\d{1,2}([Tt]|\s+)\d{1,2}:\d\d:\d\d(\.\d*)?(\s*Z|[-+]\d{1,2}(:\d\d)?)?/
|
||||
|
||||
# Create a new scanner
|
||||
def initialize
|
||||
@string_cache = {}
|
||||
end
|
||||
|
||||
# Tokenize +string+ returning the ruby object
|
||||
def tokenize string
|
||||
return nil if string.empty?
|
||||
return string if @string_cache.key?(string)
|
||||
|
||||
case string
|
||||
when /^[A-Za-z~]/
|
||||
if string.length > 5
|
||||
@string_cache[string] = true
|
||||
return string
|
||||
end
|
||||
|
||||
case string
|
||||
when /^[^ytonf~]/i
|
||||
@string_cache[string] = true
|
||||
string
|
||||
when '~', /^null$/i
|
||||
nil
|
||||
when /^(yes|true|on)$/i
|
||||
true
|
||||
when /^(no|false|off)$/i
|
||||
false
|
||||
else
|
||||
@string_cache[string] = true
|
||||
string
|
||||
end
|
||||
when TIME
|
||||
date, time = *(string.split(/[ tT]/, 2))
|
||||
(yy, m, dd) = date.split('-').map { |x| x.to_i }
|
||||
md = time.match(/(\d+:\d+:\d+)(\.\d*)?\s*(Z|[-+]\d+(:\d\d)?)?/)
|
||||
|
||||
(hh, mm, ss) = md[1].split(':').map { |x| x.to_i }
|
||||
us = (md[2] ? Rational(md[2].sub(/^\./, '0.')) : 0) * 1000000
|
||||
|
||||
time = Time.utc(yy, m, dd, hh, mm, ss, us)
|
||||
|
||||
return time if 'Z' == md[3]
|
||||
|
||||
tz = md[3] ? Integer(md[3].split(':').first.sub(/([-+])0/, '\1')) : 0
|
||||
Time.at((time - (tz * 3600)).to_i, us)
|
||||
when /^\d{4}-\d{1,2}-\d{1,2}$/
|
||||
require 'date'
|
||||
Date.strptime(string, '%Y-%m-%d')
|
||||
when /^\.inf$/i
|
||||
1 / 0.0
|
||||
when /^-\.inf$/i
|
||||
-1 / 0.0
|
||||
when /^\.nan$/i
|
||||
0.0 / 0.0
|
||||
when /^:./
|
||||
if string =~ /^:(["'])(.*)\1/
|
||||
$2.sub(/^:/, '').to_sym
|
||||
else
|
||||
string.sub(/^:/, '').to_sym
|
||||
end
|
||||
when /^[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+$/
|
||||
i = 0
|
||||
string.split(':').each_with_index do |n,e|
|
||||
i += (n.to_i * 60 ** (e - 2).abs)
|
||||
end
|
||||
i
|
||||
when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\.[0-9_]*$/
|
||||
i = 0
|
||||
string.split(':').each_with_index do |n,e|
|
||||
i += (n.to_f * 60 ** (e - 2).abs)
|
||||
end
|
||||
i
|
||||
else
|
||||
return Integer(string.gsub(/[,_]/, '')) rescue ArgumentError
|
||||
return Float(string.gsub(/[,_]/, '')) rescue ArgumentError
|
||||
@string_cache[string] = true
|
||||
string
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
4
lib/psych/set.rb
Normal file
4
lib/psych/set.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
module Psych
|
||||
class Set < ::Hash
|
||||
end
|
||||
end
|
89
lib/psych/tree_builder.rb
Normal file
89
lib/psych/tree_builder.rb
Normal file
|
@ -0,0 +1,89 @@
|
|||
require 'psych/handler'
|
||||
|
||||
module Psych
|
||||
###
|
||||
# This class works in conjunction with Psych::Parser to build an in-memory
|
||||
# parse tree tree that represents a YAML document.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# parser = Psych::Parser.new Psych::TreeBuilder.new
|
||||
# parser.parse('--- foo')
|
||||
# tree = parser.handler.root
|
||||
#
|
||||
# See Psych::Handler for documentation on the event methods used in this
|
||||
# class.
|
||||
class TreeBuilder < Psych::Handler
|
||||
# Create a new TreeBuilder instance
|
||||
def initialize
|
||||
@stack = []
|
||||
@last = nil
|
||||
end
|
||||
|
||||
# Returns the root node for the built tree
|
||||
def root
|
||||
@stack.first
|
||||
end
|
||||
|
||||
%w{
|
||||
Sequence
|
||||
Mapping
|
||||
}.each do |node|
|
||||
class_eval %{
|
||||
def start_#{node.downcase}(anchor, tag, implicit, style)
|
||||
n = Nodes::#{node}.new(anchor, tag, implicit, style)
|
||||
@last.children << n
|
||||
push n
|
||||
end
|
||||
|
||||
def end_#{node.downcase}
|
||||
pop
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
###
|
||||
# Handles start_document events with +version+, +tag_directives+,
|
||||
# and +implicit+ styling.
|
||||
#
|
||||
# See Psych::Handler#start_document
|
||||
def start_document version, tag_directives, implicit
|
||||
n = Nodes::Document.new(version, tag_directives, implicit)
|
||||
@last.children << n
|
||||
push n
|
||||
end
|
||||
|
||||
###
|
||||
# Handles end_document events with +version+, +tag_directives+,
|
||||
# and +implicit+ styling.
|
||||
#
|
||||
# See Psych::Handler#start_document
|
||||
def end_document implicit_end
|
||||
@last.implicit_end = implicit_end
|
||||
pop
|
||||
end
|
||||
|
||||
def start_stream encoding
|
||||
push Nodes::Stream.new(encoding)
|
||||
end
|
||||
|
||||
def scalar value, anchor, tag, plain, quoted, style
|
||||
@last.children << Nodes::Scalar.new(value,anchor,tag,plain,quoted,style)
|
||||
end
|
||||
|
||||
def alias anchor
|
||||
@last.children << Nodes::Alias.new(anchor)
|
||||
end
|
||||
|
||||
private
|
||||
def push value
|
||||
@stack.push value
|
||||
@last = value
|
||||
end
|
||||
|
||||
def pop
|
||||
@stack.pop
|
||||
@last = @stack.last
|
||||
end
|
||||
end
|
||||
end
|
5
lib/psych/visitors.rb
Normal file
5
lib/psych/visitors.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'psych/visitors/visitor'
|
||||
require 'psych/visitors/to_ruby'
|
||||
require 'psych/visitors/emitter'
|
||||
require 'psych/visitors/yaml_tree'
|
||||
require 'psych/visitors/json_tree'
|
41
lib/psych/visitors/emitter.rb
Normal file
41
lib/psych/visitors/emitter.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
module Psych
|
||||
module Visitors
|
||||
class Emitter < Psych::Visitors::Visitor
|
||||
def initialize io
|
||||
@handler = Psych::Emitter.new io
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Stream o
|
||||
@handler.start_stream o.encoding
|
||||
o.children.each { |c| accept c }
|
||||
@handler.end_stream
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Document o
|
||||
@handler.start_document o.version, o.tag_directives, o.implicit
|
||||
o.children.each { |c| accept c }
|
||||
@handler.end_document o.implicit_end
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Scalar o
|
||||
@handler.scalar o.value, o.anchor, o.tag, o.plain, o.quoted, o.style
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Sequence o
|
||||
@handler.start_sequence o.anchor, o.tag, o.implicit, o.style
|
||||
o.children.each { |c| accept c }
|
||||
@handler.end_sequence
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Mapping o
|
||||
@handler.start_mapping o.anchor, o.tag, o.implicit, o.style
|
||||
o.children.each { |c| accept c }
|
||||
@handler.end_mapping
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Alias o
|
||||
@handler.alias o.anchor
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
37
lib/psych/visitors/json_tree.rb
Normal file
37
lib/psych/visitors/json_tree.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
module Psych
|
||||
module Visitors
|
||||
class JSONTree < YAMLTree
|
||||
def visit_Symbol o
|
||||
append create_scalar o.to_s
|
||||
end
|
||||
|
||||
def visit_NilClass o
|
||||
scalar = Nodes::Scalar.new(
|
||||
'null', nil, nil, true, false, Nodes::Scalar::PLAIN)
|
||||
append scalar
|
||||
end
|
||||
|
||||
private
|
||||
def create_document
|
||||
doc = super
|
||||
doc.implicit = true
|
||||
doc.implicit_end = true
|
||||
doc
|
||||
end
|
||||
|
||||
def create_mapping
|
||||
map = super
|
||||
map.style = Nodes::Mapping::FLOW
|
||||
map
|
||||
end
|
||||
|
||||
def create_scalar value, anchor = nil, tag = nil, plain = false, quoted = true, style = Nodes::Scalar::ANY
|
||||
super(value, anchor, tag, false, true, style)
|
||||
end
|
||||
|
||||
def create_sequence anchor = nil, tag = nil, implicit = true, style = Nodes::Sequence::FLOW
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
252
lib/psych/visitors/to_ruby.rb
Normal file
252
lib/psych/visitors/to_ruby.rb
Normal file
|
@ -0,0 +1,252 @@
|
|||
require 'psych/scalar_scanner'
|
||||
|
||||
module Psych
|
||||
module Visitors
|
||||
###
|
||||
# This class walks a YAML AST, converting each node to ruby
|
||||
class ToRuby < Psych::Visitors::Visitor
|
||||
def initialize
|
||||
super
|
||||
@st = {}
|
||||
@ss = ScalarScanner.new
|
||||
@domain_types = Psych.domain_types
|
||||
end
|
||||
|
||||
def accept target
|
||||
result = super
|
||||
return result if @domain_types.empty? || !target.tag
|
||||
|
||||
short_name = target.tag.sub(/^!/, '').split('/', 2).last
|
||||
if Psych.domain_types.key? short_name
|
||||
url, block = Psych.domain_types[short_name]
|
||||
return block.call "#{url}:#{short_name}", result
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Scalar o
|
||||
@st[o.anchor] = o.value if o.anchor
|
||||
|
||||
if klass = Psych.load_tags[o.tag]
|
||||
instance = klass.allocate
|
||||
|
||||
if instance.respond_to?(:init_with)
|
||||
coder = Psych::Coder.new(o.tag)
|
||||
coder.scalar = o.value
|
||||
instance.init_with coder
|
||||
end
|
||||
|
||||
return instance
|
||||
end
|
||||
|
||||
return o.value if o.quoted
|
||||
return @ss.tokenize(o.value) unless o.tag
|
||||
|
||||
case o.tag
|
||||
when '!binary', 'tag:yaml.org,2002:binary'
|
||||
o.value.unpack('m').first
|
||||
when '!str', 'tag:yaml.org,2002:str'
|
||||
o.value
|
||||
when "!ruby/object:Complex"
|
||||
Complex(o.value)
|
||||
when "!ruby/object:Rational"
|
||||
Rational(o.value)
|
||||
when "tag:yaml.org,2002:float", "!float"
|
||||
Float(@ss.tokenize(o.value))
|
||||
when "!ruby/regexp"
|
||||
o.value =~ /^\/(.*)\/([mix]*)$/
|
||||
source = $1
|
||||
options = 0
|
||||
lang = nil
|
||||
($2 || '').split('').each do |option|
|
||||
case option
|
||||
when 'x' then options |= Regexp::EXTENDED
|
||||
when 'i' then options |= Regexp::IGNORECASE
|
||||
when 'm' then options |= Regexp::MULTILINE
|
||||
else lang = option
|
||||
end
|
||||
end
|
||||
Regexp.new(*[source, options, lang].compact)
|
||||
when "!ruby/range"
|
||||
args = o.value.split(/([.]{2,3})/, 2).map { |s|
|
||||
accept Nodes::Scalar.new(s)
|
||||
}
|
||||
args.push(args.delete_at(1) == '...')
|
||||
Range.new(*args)
|
||||
when /^!ruby\/sym(bol)?:?(.*)?$/
|
||||
o.value.to_sym
|
||||
else
|
||||
@ss.tokenize o.value
|
||||
end
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Sequence o
|
||||
if klass = Psych.load_tags[o.tag]
|
||||
instance = klass.allocate
|
||||
|
||||
if instance.respond_to?(:init_with)
|
||||
coder = Psych::Coder.new(o.tag)
|
||||
coder.seq = o.children.map { |c| accept c }
|
||||
instance.init_with coder
|
||||
end
|
||||
|
||||
return instance
|
||||
end
|
||||
|
||||
case o.tag
|
||||
when '!omap', 'tag:yaml.org,2002:omap'
|
||||
map = Psych::Omap.new
|
||||
@st[o.anchor] = map if o.anchor
|
||||
o.children.each { |a|
|
||||
map[accept(a.children.first)] = accept a.children.last
|
||||
}
|
||||
map
|
||||
else
|
||||
list = []
|
||||
@st[o.anchor] = list if o.anchor
|
||||
o.children.each { |c| list.push accept c }
|
||||
list
|
||||
end
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Mapping o
|
||||
return revive(Psych.load_tags[o.tag], o) if Psych.load_tags[o.tag]
|
||||
|
||||
case o.tag
|
||||
when '!str', 'tag:yaml.org,2002:str'
|
||||
members = Hash[*o.children.map { |c| accept c }]
|
||||
string = members.delete 'str'
|
||||
init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o)
|
||||
when /^!ruby\/struct:?(.*)?$/
|
||||
klass = resolve_class($1)
|
||||
|
||||
if klass
|
||||
s = klass.allocate
|
||||
@st[o.anchor] = s if o.anchor
|
||||
|
||||
members = {}
|
||||
struct_members = s.members.map { |x| x.to_sym }
|
||||
o.children.each_slice(2) do |k,v|
|
||||
member = accept(k)
|
||||
value = accept(v)
|
||||
if struct_members.include?(member.to_sym)
|
||||
s.send("#{member}=", value)
|
||||
else
|
||||
members[member.to_s.sub(/^@/, '')] = value
|
||||
end
|
||||
end
|
||||
init_with(s, members, o)
|
||||
else
|
||||
members = o.children.map { |c| accept c }
|
||||
h = Hash[*members]
|
||||
Struct.new(*h.map { |k,v| k.to_sym }).new(*h.map { |k,v| v })
|
||||
end
|
||||
|
||||
when '!ruby/range'
|
||||
h = Hash[*o.children.map { |c| accept c }]
|
||||
Range.new(h['begin'], h['end'], h['excl'])
|
||||
|
||||
when /^!ruby\/exception:?(.*)?$/
|
||||
h = Hash[*o.children.map { |c| accept c }]
|
||||
|
||||
e = build_exception((resolve_class($1) || Exception),
|
||||
h.delete('message'))
|
||||
init_with(e, h, o)
|
||||
|
||||
when '!set', 'tag:yaml.org,2002:set'
|
||||
set = Psych::Set.new
|
||||
@st[o.anchor] = set if o.anchor
|
||||
o.children.each_slice(2) do |k,v|
|
||||
set[accept(k)] = accept(v)
|
||||
end
|
||||
set
|
||||
|
||||
when '!ruby/object:Complex'
|
||||
h = Hash[*o.children.map { |c| accept c }]
|
||||
Complex(h['real'], h['image'])
|
||||
|
||||
when '!ruby/object:Rational'
|
||||
h = Hash[*o.children.map { |c| accept c }]
|
||||
Rational(h['numerator'], h['denominator'])
|
||||
|
||||
when /^!ruby\/object:?(.*)?$/
|
||||
name = $1 || 'Object'
|
||||
obj = revive((resolve_class(name) || Object), o)
|
||||
@st[o.anchor] = obj if o.anchor
|
||||
obj
|
||||
else
|
||||
hash = {}
|
||||
@st[o.anchor] = hash if o.anchor
|
||||
|
||||
o.children.each_slice(2) { |k,v|
|
||||
key = accept(k)
|
||||
|
||||
if key == '<<' && Nodes::Alias === v
|
||||
# FIXME: remove this when "<<" syntax is deprecated
|
||||
if $VERBOSE
|
||||
where = caller.find { |x| x !~ /psych/ }
|
||||
warn where
|
||||
warn "\"<<: *#{v.anchor}\" is no longer supported, please switch to \"*#{v.anchor}\""
|
||||
end
|
||||
return accept(v)
|
||||
else
|
||||
hash[key] = accept(v)
|
||||
end
|
||||
|
||||
}
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Document o
|
||||
accept o.root
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Stream o
|
||||
o.children.map { |c| accept c }
|
||||
end
|
||||
|
||||
def visit_Psych_Nodes_Alias o
|
||||
@st[o.anchor]
|
||||
end
|
||||
|
||||
private
|
||||
def revive klass, node
|
||||
s = klass.allocate
|
||||
h = Hash[*node.children.map { |c| accept c }]
|
||||
init_with(s, h, node)
|
||||
end
|
||||
|
||||
def init_with o, h, node
|
||||
if o.respond_to?(:init_with)
|
||||
c = Psych::Coder.new(node.tag)
|
||||
c.map = h
|
||||
o.init_with c
|
||||
else
|
||||
h.each { |k,v| o.instance_variable_set(:"@#{k}", v) }
|
||||
end
|
||||
o
|
||||
end
|
||||
|
||||
# Convert +klassname+ to a Class
|
||||
def resolve_class klassname
|
||||
return nil unless klassname and not klassname.empty?
|
||||
|
||||
name = klassname
|
||||
retried = false
|
||||
|
||||
begin
|
||||
path2class(name)
|
||||
rescue ArgumentError => ex
|
||||
name = "Struct::#{name}"
|
||||
unless retried
|
||||
retried = true
|
||||
retry
|
||||
end
|
||||
raise ex
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
18
lib/psych/visitors/visitor.rb
Normal file
18
lib/psych/visitors/visitor.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
module Psych
|
||||
module Visitors
|
||||
class Visitor
|
||||
def accept target
|
||||
case target
|
||||
when Psych::Nodes::Scalar then visit_Psych_Nodes_Scalar target
|
||||
when Psych::Nodes::Mapping then visit_Psych_Nodes_Mapping target
|
||||
when Psych::Nodes::Sequence then visit_Psych_Nodes_Sequence target
|
||||
when Psych::Nodes::Alias then visit_Psych_Nodes_Alias target
|
||||
when Psych::Nodes::Document then visit_Psych_Nodes_Document target
|
||||
when Psych::Nodes::Stream then visit_Psych_Nodes_Stream target
|
||||
else
|
||||
raise "Can't handle #{target}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
312
lib/psych/visitors/yaml_tree.rb
Normal file
312
lib/psych/visitors/yaml_tree.rb
Normal file
|
@ -0,0 +1,312 @@
|
|||
module Psych
|
||||
module Visitors
|
||||
class YAMLTree < Psych::Visitors::Visitor
|
||||
attr_reader :tree
|
||||
|
||||
def initialize options = {}
|
||||
super()
|
||||
@json = options[:json]
|
||||
@tree = Nodes::Stream.new
|
||||
@stack = []
|
||||
@st = {}
|
||||
@ss = ScalarScanner.new
|
||||
|
||||
@dispatch_cache = Hash.new do |h,klass|
|
||||
method = "visit_#{(klass.name || '').split('::').join('_')}"
|
||||
|
||||
method = respond_to?(method) ? method : h[klass.superclass]
|
||||
|
||||
raise(TypeError, "Can't dump #{target.class}") unless method
|
||||
|
||||
h[klass] = method
|
||||
end
|
||||
end
|
||||
|
||||
def << object
|
||||
doc = create_document
|
||||
@stack << doc
|
||||
@tree.children << doc
|
||||
accept object
|
||||
end
|
||||
|
||||
def accept target
|
||||
# return any aliases we find
|
||||
if node = @st[target.object_id]
|
||||
node.anchor = target.object_id.to_s
|
||||
return append Nodes::Alias.new target.object_id.to_s
|
||||
end
|
||||
|
||||
if target.respond_to?(:encode_with)
|
||||
dump_coder target
|
||||
else
|
||||
send(@dispatch_cache[target.class], target)
|
||||
end
|
||||
end
|
||||
|
||||
def visit_Psych_Omap o
|
||||
seq = Nodes::Sequence.new(nil, '!omap', false)
|
||||
register(o, seq)
|
||||
|
||||
@stack.push append seq
|
||||
o.each { |k,v| visit_Hash k => v }
|
||||
@stack.pop
|
||||
end
|
||||
|
||||
def visit_Object o
|
||||
tag = Psych.dump_tags[o.class]
|
||||
unless tag
|
||||
klass = o.class == Object ? nil : o.class.name
|
||||
tag = ['!ruby/object', klass].compact.join(':')
|
||||
end
|
||||
|
||||
map = append Nodes::Mapping.new(nil, tag, false)
|
||||
register(o, map)
|
||||
|
||||
@stack.push map
|
||||
dump_ivars(o, map)
|
||||
@stack.pop
|
||||
end
|
||||
|
||||
def visit_Struct o
|
||||
tag = ['!ruby/struct', o.class.name].compact.join(':')
|
||||
|
||||
map = register(o, Nodes::Mapping.new(nil, tag, false))
|
||||
|
||||
@stack.push append map
|
||||
|
||||
o.members.each do |member|
|
||||
map.children << Nodes::Scalar.new("#{member}")
|
||||
accept o[member]
|
||||
end
|
||||
|
||||
dump_ivars(o, map)
|
||||
|
||||
@stack.pop
|
||||
end
|
||||
|
||||
def visit_Exception o
|
||||
tag = ['!ruby/exception', o.class.name].join ':'
|
||||
|
||||
map = append Nodes::Mapping.new(nil, tag, false)
|
||||
|
||||
@stack.push map
|
||||
|
||||
{
|
||||
'message' => private_iv_get(o, 'mesg'),
|
||||
'backtrace' => private_iv_get(o, 'backtrace'),
|
||||
}.each do |k,v|
|
||||
next unless v
|
||||
map.children << Nodes::Scalar.new(k)
|
||||
accept v
|
||||
end
|
||||
|
||||
dump_ivars(o, map)
|
||||
|
||||
@stack.pop
|
||||
end
|
||||
|
||||
def visit_Regexp o
|
||||
append Nodes::Scalar.new(o.inspect, nil, '!ruby/regexp', false)
|
||||
end
|
||||
|
||||
def visit_Time o
|
||||
formatted = o.strftime("%Y-%m-%d %H:%M:%S")
|
||||
if o.utc?
|
||||
formatted += ".%06dZ" % [o.usec]
|
||||
else
|
||||
formatted += ".%06d %+.2d:00" % [o.usec, o.gmt_offset / 3600]
|
||||
end
|
||||
|
||||
append Nodes::Scalar.new formatted
|
||||
end
|
||||
|
||||
def visit_Rational o
|
||||
map = append Nodes::Mapping.new(nil, '!ruby/object:Rational', false)
|
||||
[
|
||||
'denominator', o.denominator.to_s,
|
||||
'numerator', o.numerator.to_s
|
||||
].each do |m|
|
||||
map.children << Nodes::Scalar.new(m)
|
||||
end
|
||||
end
|
||||
|
||||
def visit_Complex o
|
||||
map = append Nodes::Mapping.new(nil, '!ruby/object:Complex', false)
|
||||
|
||||
['real', o.real.to_s, 'image', o.imag.to_s].each do |m|
|
||||
map.children << Nodes::Scalar.new(m)
|
||||
end
|
||||
end
|
||||
|
||||
def visit_Integer o
|
||||
append Nodes::Scalar.new o.to_s
|
||||
end
|
||||
alias :visit_TrueClass :visit_Integer
|
||||
alias :visit_FalseClass :visit_Integer
|
||||
alias :visit_Date :visit_Integer
|
||||
|
||||
def visit_Float o
|
||||
if o.nan?
|
||||
append Nodes::Scalar.new '.nan'
|
||||
elsif o.infinite?
|
||||
append Nodes::Scalar.new(o.infinite? > 0 ? '.inf' : '-.inf')
|
||||
else
|
||||
append Nodes::Scalar.new o.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def visit_String o
|
||||
plain = false
|
||||
quote = false
|
||||
|
||||
if o.index("\x00") || o.count("^ -~\t\r\n").fdiv(o.length) > 0.3
|
||||
str = [o].pack('m').chomp
|
||||
tag = '!binary'
|
||||
else
|
||||
str = o
|
||||
tag = nil
|
||||
quote = !(String === @ss.tokenize(o))
|
||||
plain = !quote
|
||||
end
|
||||
|
||||
ivars = o.respond_to?(:to_yaml_properties) ?
|
||||
o.to_yaml_properties :
|
||||
o.instance_variables
|
||||
|
||||
scalar = create_scalar str, nil, tag, plain, quote
|
||||
|
||||
if ivars.empty?
|
||||
append scalar
|
||||
else
|
||||
mapping = append Nodes::Mapping.new(nil, '!str', false)
|
||||
|
||||
mapping.children << Nodes::Scalar.new('str')
|
||||
mapping.children << scalar
|
||||
|
||||
@stack.push mapping
|
||||
dump_ivars o, mapping
|
||||
@stack.pop
|
||||
end
|
||||
end
|
||||
|
||||
def visit_Class o
|
||||
raise TypeError, "can't dump anonymous class #{o.class}"
|
||||
end
|
||||
|
||||
def visit_Range o
|
||||
@stack.push append Nodes::Mapping.new(nil, '!ruby/range', false)
|
||||
['begin', o.begin, 'end', o.end, 'excl', o.exclude_end?].each do |m|
|
||||
accept m
|
||||
end
|
||||
@stack.pop
|
||||
end
|
||||
|
||||
def visit_Hash o
|
||||
@stack.push append register(o, create_mapping)
|
||||
|
||||
o.each do |k,v|
|
||||
accept k
|
||||
accept v
|
||||
end
|
||||
|
||||
@stack.pop
|
||||
end
|
||||
|
||||
def visit_Psych_Set o
|
||||
@stack.push append register(o, Nodes::Mapping.new(nil, '!set', false))
|
||||
|
||||
o.each do |k,v|
|
||||
accept k
|
||||
accept v
|
||||
end
|
||||
|
||||
@stack.pop
|
||||
end
|
||||
|
||||
def visit_Array o
|
||||
@stack.push append register(o, create_sequence)
|
||||
o.each { |c| accept c }
|
||||
@stack.pop
|
||||
end
|
||||
|
||||
def visit_NilClass o
|
||||
append Nodes::Scalar.new('', nil, 'tag:yaml.org,2002:null', false)
|
||||
end
|
||||
|
||||
def visit_Symbol o
|
||||
append create_scalar ":#{o}"
|
||||
end
|
||||
|
||||
private
|
||||
def append o
|
||||
@stack.last.children << o
|
||||
o
|
||||
end
|
||||
|
||||
def register target, yaml_obj
|
||||
@st[target.object_id] = yaml_obj
|
||||
yaml_obj
|
||||
end
|
||||
|
||||
def dump_coder o
|
||||
tag = Psych.dump_tags[o.class]
|
||||
unless tag
|
||||
klass = o.class == Object ? nil : o.class.name
|
||||
tag = ['!ruby/object', klass].compact.join(':')
|
||||
end
|
||||
|
||||
c = Psych::Coder.new(tag)
|
||||
o.encode_with(c)
|
||||
emit_coder c
|
||||
end
|
||||
|
||||
def emit_coder c
|
||||
case c.type
|
||||
when :scalar
|
||||
append create_scalar(c.scalar, nil, c.tag, c.tag.nil?)
|
||||
when :seq
|
||||
@stack.push append create_sequence(nil, c.tag, c.tag.nil?)
|
||||
c.seq.each do |thing|
|
||||
accept thing
|
||||
end
|
||||
@stack.pop
|
||||
when :map
|
||||
map = append Nodes::Mapping.new(nil, c.tag, c.implicit, c.style)
|
||||
@stack.push map
|
||||
c.map.each do |k,v|
|
||||
map.children << create_scalar(k)
|
||||
accept v
|
||||
end
|
||||
@stack.pop
|
||||
end
|
||||
end
|
||||
|
||||
def dump_ivars target, map
|
||||
ivars = target.respond_to?(:to_yaml_properties) ?
|
||||
target.to_yaml_properties :
|
||||
target.instance_variables
|
||||
|
||||
ivars.each do |iv|
|
||||
map.children << create_scalar("#{iv.to_s.sub(/^@/, '')}")
|
||||
accept target.instance_variable_get(iv)
|
||||
end
|
||||
end
|
||||
|
||||
def create_document
|
||||
Nodes::Document.new
|
||||
end
|
||||
|
||||
def create_mapping
|
||||
Nodes::Mapping.new
|
||||
end
|
||||
|
||||
def create_scalar value, anchor = nil, tag = nil, plain = true, quoted = false, style = Nodes::Scalar::ANY
|
||||
Nodes::Scalar.new(value, anchor, tag, plain, quoted, style)
|
||||
end
|
||||
|
||||
def create_sequence anchor = nil, tag = nil, implicit = true, style = Nodes::Sequence::BLOCK
|
||||
Nodes::Sequence.new(anchor, tag, implicit, style)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
63
test/psych/helper.rb
Normal file
63
test/psych/helper.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
require 'minitest/autorun'
|
||||
require 'stringio'
|
||||
require 'tempfile'
|
||||
require 'date'
|
||||
|
||||
module Psych
|
||||
class TestCase < MiniTest::Unit::TestCase
|
||||
#
|
||||
# Convert between Psych and the object to verify correct parsing and
|
||||
# emitting
|
||||
#
|
||||
def assert_to_yaml( obj, yaml )
|
||||
assert_equal( obj, Psych::load( yaml ) )
|
||||
assert_equal( obj, Psych::parse( yaml ).transform )
|
||||
assert_equal( obj, Psych::load( obj.psych_to_yaml ) )
|
||||
assert_equal( obj, Psych::parse( obj.psych_to_yaml ).transform )
|
||||
assert_equal( obj, Psych::load(
|
||||
obj.psych_to_yaml(
|
||||
:UseVersion => true, :UseHeader => true, :SortKeys => true
|
||||
)
|
||||
))
|
||||
end
|
||||
|
||||
#
|
||||
# Test parser only
|
||||
#
|
||||
def assert_parse_only( obj, yaml )
|
||||
assert_equal( obj, Psych::load( yaml ) )
|
||||
assert_equal( obj, Psych::parse( yaml ).transform )
|
||||
end
|
||||
|
||||
def assert_cycle( obj )
|
||||
v = Visitors::YAMLTree.new
|
||||
v << obj
|
||||
assert_equal(obj, Psych.load(v.tree.to_yaml))
|
||||
assert_equal( obj, Psych::load(Psych.dump(obj)))
|
||||
assert_equal( obj, Psych::load( obj.psych_to_yaml ) )
|
||||
end
|
||||
|
||||
#
|
||||
# Make a time with the time zone
|
||||
#
|
||||
def mktime( year, mon, day, hour, min, sec, usec, zone = "Z" )
|
||||
usec = Rational(usec.to_s) * 1000000
|
||||
val = Time::utc( year.to_i, mon.to_i, day.to_i, hour.to_i, min.to_i, sec.to_i, usec )
|
||||
if zone != "Z"
|
||||
hour = zone[0,3].to_i * 3600
|
||||
min = zone[3,2].to_i * 60
|
||||
ofs = (hour + min)
|
||||
val = Time.at( val.tv_sec - ofs, val.tv_nsec / 1000.0 )
|
||||
end
|
||||
return val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'psych'
|
||||
|
||||
# FIXME: remove this when syck is removed
|
||||
o = Object.new
|
||||
a = o.method(:psych_to_yaml)
|
||||
b = o.method(:to_yaml)
|
||||
raise "psych should define to_yaml" unless a == b
|
26
test/psych/test_alias_and_anchor.rb
Normal file
26
test/psych/test_alias_and_anchor.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestAliasAndAnchor < TestCase
|
||||
def test_mri_compatibility
|
||||
yaml = <<EOYAML
|
||||
---
|
||||
- &id001 !ruby/object {}
|
||||
|
||||
- *id001
|
||||
- *id001
|
||||
EOYAML
|
||||
result = Psych.load yaml
|
||||
result.each {|el| assert_same(result[0], el) }
|
||||
end
|
||||
|
||||
def test_anchor_alias_round_trip
|
||||
o = Object.new
|
||||
original = [o,o,o]
|
||||
|
||||
yaml = Psych.dump original
|
||||
result = Psych.load yaml
|
||||
result.each {|el| assert_same(result[0], el) }
|
||||
end
|
||||
end
|
||||
end
|
19
test/psych/test_array.rb
Normal file
19
test/psych/test_array.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestArray < TestCase
|
||||
def setup
|
||||
super
|
||||
@list = [{ :a => 'b' }, 'foo']
|
||||
end
|
||||
|
||||
def test_self_referential
|
||||
@list << @list
|
||||
assert_cycle(@list)
|
||||
end
|
||||
|
||||
def test_cycle
|
||||
assert_cycle(@list)
|
||||
end
|
||||
end
|
||||
end
|
36
test/psych/test_boolean.rb
Normal file
36
test/psych/test_boolean.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
###
|
||||
# Test booleans from YAML spec:
|
||||
# http://yaml.org/type/bool.html
|
||||
class TestBoolean < TestCase
|
||||
%w{ yes Yes YES true True TRUE on On ON }.each do |truth|
|
||||
define_method(:"test_#{truth}") do
|
||||
assert_equal true, Psych.load("--- #{truth}")
|
||||
end
|
||||
end
|
||||
|
||||
%w{ no No NO false False FALSE off Off OFF }.each do |truth|
|
||||
define_method(:"test_#{truth}") do
|
||||
assert_equal false, Psych.load("--- #{truth}")
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
# YAML spec says "y" and "Y" may be used as true, but Syck treats them
|
||||
# as literal strings
|
||||
def test_y
|
||||
assert_equal "y", Psych.load("--- y")
|
||||
assert_equal "Y", Psych.load("--- Y")
|
||||
end
|
||||
|
||||
###
|
||||
# YAML spec says "n" and "N" may be used as false, but Syck treats them
|
||||
# as literal strings
|
||||
def test_n
|
||||
assert_equal "n", Psych.load("--- n")
|
||||
assert_equal "N", Psych.load("--- N")
|
||||
end
|
||||
end
|
||||
end
|
17
test/psych/test_class.rb
Normal file
17
test/psych/test_class.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestClass < TestCase
|
||||
def test_cycle
|
||||
assert_raises(::TypeError) do
|
||||
assert_cycle(TestClass)
|
||||
end
|
||||
end
|
||||
|
||||
def test_dump
|
||||
assert_raises(::TypeError) do
|
||||
Psych.dump TestClass
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
144
test/psych/test_coder.rb
Normal file
144
test/psych/test_coder.rb
Normal file
|
@ -0,0 +1,144 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestCoder < TestCase
|
||||
class InitApi
|
||||
attr_accessor :implicit
|
||||
attr_accessor :style
|
||||
attr_accessor :tag
|
||||
attr_accessor :a, :b, :c
|
||||
|
||||
def initialize
|
||||
@a = 1
|
||||
@b = 2
|
||||
@c = 3
|
||||
end
|
||||
|
||||
def init_with coder
|
||||
@a = coder['aa']
|
||||
@b = coder['bb']
|
||||
@implicit = coder.implicit
|
||||
@tag = coder.tag
|
||||
@style = coder.style
|
||||
end
|
||||
|
||||
def encode_with coder
|
||||
coder['aa'] = @a
|
||||
coder['bb'] = @b
|
||||
end
|
||||
end
|
||||
|
||||
class TaggingCoder < InitApi
|
||||
def encode_with coder
|
||||
super
|
||||
coder.tag = coder.tag.sub(/!/, '!hello')
|
||||
coder.implicit = false
|
||||
coder.style = Psych::Nodes::Mapping::FLOW
|
||||
end
|
||||
end
|
||||
|
||||
class ScalarCoder
|
||||
def encode_with coder
|
||||
coder.scalar = "foo"
|
||||
end
|
||||
end
|
||||
|
||||
class Represent
|
||||
yaml_tag 'foo'
|
||||
def encode_with coder
|
||||
coder.represent_scalar 'foo', 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
class RepresentWithInit
|
||||
yaml_tag name
|
||||
attr_accessor :str
|
||||
|
||||
def init_with coder
|
||||
@str = coder.scalar
|
||||
end
|
||||
|
||||
def encode_with coder
|
||||
coder.represent_scalar self.class.name, 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
class RepresentWithSeq
|
||||
yaml_tag name
|
||||
attr_accessor :seq
|
||||
|
||||
def init_with coder
|
||||
@seq = coder.seq
|
||||
end
|
||||
|
||||
def encode_with coder
|
||||
coder.represent_seq self.class.name, %w{ foo bar }
|
||||
end
|
||||
end
|
||||
|
||||
class RepresentWithMap
|
||||
yaml_tag name
|
||||
attr_accessor :map
|
||||
|
||||
def init_with coder
|
||||
@map = coder.map
|
||||
end
|
||||
|
||||
def encode_with coder
|
||||
coder.represent_map self.class.name, { 'a' => 'b' }
|
||||
end
|
||||
end
|
||||
|
||||
def test_represent_map
|
||||
thing = Psych.load(Psych.dump(RepresentWithMap.new))
|
||||
assert_equal({ 'a' => 'b' }, thing.map)
|
||||
end
|
||||
|
||||
def test_represent_sequence
|
||||
thing = Psych.load(Psych.dump(RepresentWithSeq.new))
|
||||
assert_equal %w{ foo bar }, thing.seq
|
||||
end
|
||||
|
||||
def test_represent_with_init
|
||||
thing = Psych.load(Psych.dump(RepresentWithInit.new))
|
||||
assert_equal 'bar', thing.str
|
||||
end
|
||||
|
||||
def test_represent!
|
||||
assert_match(/foo/, Psych.dump(Represent.new))
|
||||
assert_instance_of(Represent, Psych.load(Psych.dump(Represent.new)))
|
||||
end
|
||||
|
||||
def test_scalar_coder
|
||||
foo = Psych.load(Psych.dump(ScalarCoder.new))
|
||||
assert_equal 'foo', foo
|
||||
end
|
||||
|
||||
def test_load_dumped_tagging
|
||||
foo = InitApi.new
|
||||
bar = Psych.load(Psych.dump(foo))
|
||||
assert_equal false, bar.implicit
|
||||
assert_equal "!ruby/object:Psych::TestCoder::InitApi", bar.tag
|
||||
assert_equal Psych::Nodes::Mapping::BLOCK, bar.style
|
||||
end
|
||||
|
||||
def test_dump_with_tag
|
||||
foo = TaggingCoder.new
|
||||
assert_match(/hello/, Psych.dump(foo))
|
||||
assert_match(/\{aa/, Psych.dump(foo))
|
||||
end
|
||||
|
||||
def test_dump_encode_with
|
||||
foo = InitApi.new
|
||||
assert_match(/aa/, Psych.dump(foo))
|
||||
end
|
||||
|
||||
def test_dump_init_with
|
||||
foo = InitApi.new
|
||||
bar = Psych.load(Psych.dump(foo))
|
||||
assert_equal foo.a, bar.a
|
||||
assert_equal foo.b, bar.b
|
||||
assert_nil bar.c
|
||||
end
|
||||
end
|
||||
end
|
46
test/psych/test_document.rb
Normal file
46
test/psych/test_document.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestDocument < TestCase
|
||||
def setup
|
||||
super
|
||||
@stream = Psych.parse_stream(<<-eoyml)
|
||||
%YAML 1.1
|
||||
%TAG ! tag:tenderlovemaking.com,2009:
|
||||
--- !fun
|
||||
eoyml
|
||||
@doc = @stream.children.first
|
||||
end
|
||||
|
||||
def test_parse_tag
|
||||
assert_equal([['!', 'tag:tenderlovemaking.com,2009:']],
|
||||
@doc.tag_directives)
|
||||
end
|
||||
|
||||
def test_emit_tag
|
||||
assert_match('%TAG ! tag:tenderlovemaking.com,2009:', @stream.to_yaml)
|
||||
end
|
||||
|
||||
def test_emit_multitag
|
||||
@doc.tag_directives << ['!!', 'foo.com,2009:']
|
||||
yaml = @stream.to_yaml
|
||||
assert_match('%TAG ! tag:tenderlovemaking.com,2009:', yaml)
|
||||
assert_match('%TAG !! foo.com,2009:', yaml)
|
||||
end
|
||||
|
||||
def test_emit_bad_tag
|
||||
assert_raises(RuntimeError) do
|
||||
@doc.tag_directives = [['!']]
|
||||
@stream.to_yaml
|
||||
end
|
||||
end
|
||||
|
||||
def test_parse_version
|
||||
assert_equal([1,1], @doc.version)
|
||||
end
|
||||
|
||||
def test_emit_version
|
||||
assert_match('%YAML 1.1', @stream.to_yaml)
|
||||
end
|
||||
end
|
||||
end
|
87
test/psych/test_emitter.rb
Normal file
87
test/psych/test_emitter.rb
Normal file
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestEmitter < TestCase
|
||||
def setup
|
||||
super
|
||||
@out = StringIO.new
|
||||
@emitter = Psych::Emitter.new @out
|
||||
end
|
||||
|
||||
def test_set_canonical
|
||||
@emitter.canonical = true
|
||||
assert_equal true, @emitter.canonical
|
||||
|
||||
@emitter.canonical = false
|
||||
assert_equal false, @emitter.canonical
|
||||
end
|
||||
|
||||
def test_indentation_set
|
||||
assert_equal 2, @emitter.indentation
|
||||
@emitter.indentation = 5
|
||||
assert_equal 5, @emitter.indentation
|
||||
end
|
||||
|
||||
def test_emit_utf_8
|
||||
@emitter.start_stream Psych::Nodes::Stream::UTF8
|
||||
@emitter.start_document [], [], false
|
||||
@emitter.scalar '日本語', nil, nil, false, true, 1
|
||||
@emitter.end_document true
|
||||
@emitter.end_stream
|
||||
assert_match('日本語', @out.string)
|
||||
end
|
||||
|
||||
def test_start_stream_arg_error
|
||||
assert_raises(TypeError) do
|
||||
@emitter.start_stream 'asdfasdf'
|
||||
end
|
||||
end
|
||||
|
||||
def test_start_doc_arg_error
|
||||
@emitter.start_stream Psych::Nodes::Stream::UTF8
|
||||
|
||||
[
|
||||
[nil, [], false],
|
||||
[[nil, nil], [], false],
|
||||
[[], 'foo', false],
|
||||
[[], ['foo'], false],
|
||||
[[], [nil,nil], false],
|
||||
].each do |args|
|
||||
assert_raises(TypeError) do
|
||||
@emitter.start_document(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_scalar_arg_error
|
||||
@emitter.start_stream Psych::Nodes::Stream::UTF8
|
||||
@emitter.start_document [], [], false
|
||||
|
||||
[
|
||||
[:foo, nil, nil, false, true, 1],
|
||||
['foo', Object.new, nil, false, true, 1],
|
||||
['foo', nil, Object.new, false, true, 1],
|
||||
['foo', nil, nil, false, true, :foo],
|
||||
].each do |args|
|
||||
assert_raises(TypeError) do
|
||||
@emitter.scalar(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_start_sequence_arg_error
|
||||
@emitter.start_stream Psych::Nodes::Stream::UTF8
|
||||
@emitter.start_document [], [], false
|
||||
|
||||
assert_raises(TypeError) do
|
||||
@emitter.start_sequence(nil, Object.new, true, 1)
|
||||
end
|
||||
|
||||
assert_raises(TypeError) do
|
||||
@emitter.start_sequence(nil, nil, true, :foo)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
91
test/psych/test_encoding.rb
Normal file
91
test/psych/test_encoding.rb
Normal file
|
@ -0,0 +1,91 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestEncoding < TestCase
|
||||
class EncodingCatcher < Handler
|
||||
attr_reader :strings
|
||||
def initialize
|
||||
@strings = []
|
||||
end
|
||||
|
||||
(Handler.instance_methods(true) -
|
||||
Object.instance_methods).each do |m|
|
||||
class_eval %{
|
||||
def #{m} *args
|
||||
@strings += args.flatten.find_all { |a|
|
||||
String === a
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
super
|
||||
@handler = EncodingCatcher.new
|
||||
@parser = Psych::Parser.new @handler
|
||||
@utf8 = Encoding.find('UTF-8')
|
||||
end
|
||||
|
||||
def test_scalar
|
||||
@parser.parse("--- a")
|
||||
assert_encodings @utf8, @handler.strings
|
||||
end
|
||||
|
||||
def test_alias
|
||||
@parser.parse(<<-eoyml)
|
||||
%YAML 1.1
|
||||
---
|
||||
!!seq [
|
||||
!!str "Without properties",
|
||||
&A !!str "Anchored",
|
||||
!!str "Tagged",
|
||||
*A,
|
||||
!!str "",
|
||||
]
|
||||
eoyml
|
||||
assert_encodings @utf8, @handler.strings
|
||||
end
|
||||
|
||||
def test_list_anchor
|
||||
list = %w{ a b }
|
||||
list << list
|
||||
@parser.parse(Psych.dump(list))
|
||||
assert_encodings @utf8, @handler.strings
|
||||
end
|
||||
|
||||
def test_map_anchor
|
||||
h = {}
|
||||
h['a'] = h
|
||||
@parser.parse(Psych.dump(h))
|
||||
assert_encodings @utf8, @handler.strings
|
||||
end
|
||||
|
||||
def test_map_tag
|
||||
@parser.parse(<<-eoyml)
|
||||
%YAML 1.1
|
||||
---
|
||||
!!map { a : b }
|
||||
eoyml
|
||||
assert_encodings @utf8, @handler.strings
|
||||
end
|
||||
|
||||
def test_doc_tag
|
||||
@parser.parse(<<-eoyml)
|
||||
%YAML 1.1
|
||||
%TAG ! tag:tenderlovemaking.com,2009:
|
||||
--- !fun
|
||||
eoyml
|
||||
assert_encodings @utf8, @handler.strings
|
||||
end
|
||||
|
||||
private
|
||||
def assert_encodings encoding, strings
|
||||
strings.each do |str|
|
||||
assert_equal encoding, str.encoding, str
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
39
test/psych/test_exception.rb
Normal file
39
test/psych/test_exception.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestException < TestCase
|
||||
class Wups < Exception
|
||||
attr_reader :foo, :bar
|
||||
def initialize *args
|
||||
super
|
||||
@foo = 1
|
||||
@bar = 2
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
super
|
||||
@wups = Wups.new
|
||||
end
|
||||
|
||||
def test_convert
|
||||
w = Psych.load(Psych.dump(@wups))
|
||||
assert_equal @wups, w
|
||||
assert_equal 1, w.foo
|
||||
assert_equal 2, w.bar
|
||||
end
|
||||
|
||||
def test_to_yaml_properties
|
||||
class << @wups
|
||||
def to_yaml_properties
|
||||
[:@foo]
|
||||
end
|
||||
end
|
||||
|
||||
w = Psych.load(Psych.dump(@wups))
|
||||
assert_equal @wups, w
|
||||
assert_equal 1, w.foo
|
||||
assert_nil w.bar
|
||||
end
|
||||
end
|
||||
end
|
30
test/psych/test_hash.rb
Normal file
30
test/psych/test_hash.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestHash < TestCase
|
||||
def setup
|
||||
super
|
||||
@hash = { :a => 'b' }
|
||||
end
|
||||
|
||||
def test_self_referential
|
||||
@hash['self'] = @hash
|
||||
assert_cycle(@hash)
|
||||
end
|
||||
|
||||
def test_cycles
|
||||
assert_cycle(@hash)
|
||||
end
|
||||
|
||||
def test_ref_append
|
||||
hash = Psych.load(<<-eoyml)
|
||||
---
|
||||
foo: &foo
|
||||
hello: world
|
||||
bar:
|
||||
<<: *foo
|
||||
eoyml
|
||||
assert_equal({"foo"=>{"hello"=>"world"}, "bar"=>{"hello"=>"world"}}, hash)
|
||||
end
|
||||
end
|
||||
end
|
43
test/psych/test_json_tree.rb
Normal file
43
test/psych/test_json_tree.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestJSONTree < TestCase
|
||||
def test_string
|
||||
assert_match(/(['"])foo\1/, Psych.to_json("foo"))
|
||||
end
|
||||
|
||||
def test_symbol
|
||||
assert_match(/(['"])foo\1/, Psych.to_json(:foo))
|
||||
end
|
||||
|
||||
def test_nil
|
||||
assert_match(/^null/, Psych.to_json(nil))
|
||||
end
|
||||
|
||||
def test_int
|
||||
assert_match(/^10/, Psych.to_json(10))
|
||||
end
|
||||
|
||||
def test_float
|
||||
assert_match(/^1.2/, Psych.to_json(1.2))
|
||||
end
|
||||
|
||||
def test_hash
|
||||
hash = { 'one' => 'two' }
|
||||
json = Psych.to_json(hash)
|
||||
assert_match(/}$/, json)
|
||||
assert_match(/^\{/, json)
|
||||
assert_match(/['"]one['"]/, json)
|
||||
assert_match(/['"]two['"]/, json)
|
||||
end
|
||||
|
||||
def test_list_to_json
|
||||
list = %w{ one two }
|
||||
json = Psych.to_json(list)
|
||||
assert_match(/]$/, json)
|
||||
assert_match(/^\[/, json)
|
||||
assert_match(/['"]one['"]/, json)
|
||||
assert_match(/['"]two['"]/, json)
|
||||
end
|
||||
end
|
||||
end
|
19
test/psych/test_null.rb
Normal file
19
test/psych/test_null.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
###
|
||||
# Test null from YAML spec:
|
||||
# http://yaml.org/type/null.html
|
||||
class TestNull < TestCase
|
||||
def test_null_list
|
||||
assert_equal [nil] * 5, Psych.load(<<-eoyml)
|
||||
---
|
||||
- ~
|
||||
- null
|
||||
-
|
||||
- Null
|
||||
- NULL
|
||||
eoyml
|
||||
end
|
||||
end
|
||||
end
|
27
test/psych/test_object.rb
Normal file
27
test/psych/test_object.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class Tagged
|
||||
yaml_tag '!foo'
|
||||
|
||||
attr_accessor :baz
|
||||
|
||||
def initialize
|
||||
@baz = 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
class TestObject < TestCase
|
||||
def test_dump_with_tag
|
||||
tag = Tagged.new
|
||||
assert_match('foo', Psych.dump(tag))
|
||||
end
|
||||
|
||||
def test_tag_round_trip
|
||||
tag = Tagged.new
|
||||
tag2 = Psych.load(Psych.dump(tag))
|
||||
assert_equal tag.baz, tag2.baz
|
||||
assert_instance_of(Tagged, tag2)
|
||||
end
|
||||
end
|
||||
end
|
68
test/psych/test_omap.rb
Normal file
68
test/psych/test_omap.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestOmap < TestCase
|
||||
def test_self_referential
|
||||
map = Psych::Omap.new
|
||||
map['foo'] = 'bar'
|
||||
map['self'] = map
|
||||
assert_equal(map, Psych.load(Psych.dump(map)))
|
||||
end
|
||||
|
||||
def test_keys
|
||||
map = Psych::Omap.new
|
||||
map['foo'] = 'bar'
|
||||
assert_equal 'bar', map['foo']
|
||||
end
|
||||
|
||||
def test_order
|
||||
map = Psych::Omap.new
|
||||
map['a'] = 'b'
|
||||
map['b'] = 'c'
|
||||
assert_equal [%w{a b}, %w{b c}], map.to_a
|
||||
end
|
||||
|
||||
def test_square
|
||||
list = [["a", "b"], ["b", "c"]]
|
||||
map = Psych::Omap[*list.flatten]
|
||||
assert_equal list, map.to_a
|
||||
assert_equal 'b', map['a']
|
||||
assert_equal 'c', map['b']
|
||||
end
|
||||
|
||||
def test_dump
|
||||
map = Psych::Omap['a', 'b', 'c', 'd']
|
||||
yaml = Psych.dump(map)
|
||||
assert_match('!omap', yaml)
|
||||
assert_match('- a: b', yaml)
|
||||
assert_match('- c: d', yaml)
|
||||
end
|
||||
|
||||
def test_round_trip
|
||||
list = [["a", "b"], ["b", "c"]]
|
||||
map = Psych::Omap[*list.flatten]
|
||||
assert_cycle(map)
|
||||
end
|
||||
|
||||
def test_load
|
||||
list = [["a", "b"], ["c", "d"]]
|
||||
map = Psych.load(<<-eoyml)
|
||||
--- !omap
|
||||
- a: b
|
||||
- c: d
|
||||
eoyml
|
||||
assert_equal list, map.to_a
|
||||
end
|
||||
|
||||
# NOTE: This test will not work with Syck
|
||||
def test_load_shorthand
|
||||
list = [["a", "b"], ["c", "d"]]
|
||||
map = Psych.load(<<-eoyml)
|
||||
--- !!omap
|
||||
- a: b
|
||||
- c: d
|
||||
eoyml
|
||||
assert_equal list, map.to_a
|
||||
end
|
||||
end
|
||||
end
|
188
test/psych/test_parser.rb
Normal file
188
test/psych/test_parser.rb
Normal file
|
@ -0,0 +1,188 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestParser < TestCase
|
||||
class EventCatcher < Handler
|
||||
attr_reader :calls
|
||||
def initialize
|
||||
@calls = []
|
||||
end
|
||||
|
||||
(Handler.instance_methods(true) -
|
||||
Object.instance_methods).each do |m|
|
||||
class_eval %{
|
||||
def #{m} *args
|
||||
super
|
||||
@calls << [:#{m}, args]
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
super
|
||||
@parser = Psych::Parser.new EventCatcher.new
|
||||
end
|
||||
|
||||
def test_bogus_io
|
||||
o = Object.new
|
||||
def o.read len; self end
|
||||
|
||||
assert_raises(TypeError) do
|
||||
@parser.parse o
|
||||
end
|
||||
end
|
||||
|
||||
def test_parse_io
|
||||
@parser.parse StringIO.new("--- a")
|
||||
assert_called :start_stream
|
||||
assert_called :scalar
|
||||
assert_called :end_stream
|
||||
end
|
||||
|
||||
def test_syntax_error
|
||||
assert_raises(Psych::SyntaxError) do
|
||||
@parser.parse("---\n\"foo\"\n\"bar\"\n")
|
||||
end
|
||||
end
|
||||
|
||||
def test_mapping_end
|
||||
@parser.parse("---\n!!map { key: value }")
|
||||
assert_called :end_mapping
|
||||
end
|
||||
|
||||
def test_mapping_tag
|
||||
@parser.parse("---\n!!map { key: value }")
|
||||
assert_called :start_mapping, ["tag:yaml.org,2002:map", false, Nodes::Mapping::FLOW]
|
||||
end
|
||||
|
||||
def test_mapping_anchor
|
||||
@parser.parse("---\n&A { key: value }")
|
||||
assert_called :start_mapping, ['A', true, Nodes::Mapping::FLOW]
|
||||
end
|
||||
|
||||
def test_mapping_block
|
||||
@parser.parse("---\n key: value")
|
||||
assert_called :start_mapping, [true, Nodes::Mapping::BLOCK]
|
||||
end
|
||||
|
||||
def test_mapping_start
|
||||
@parser.parse("---\n{ key: value }")
|
||||
assert_called :start_mapping
|
||||
assert_called :start_mapping, [true, Nodes::Mapping::FLOW]
|
||||
end
|
||||
|
||||
def test_sequence_end
|
||||
@parser.parse("---\n&A [1, 2]")
|
||||
assert_called :end_sequence
|
||||
end
|
||||
|
||||
def test_sequence_start_anchor
|
||||
@parser.parse("---\n&A [1, 2]")
|
||||
assert_called :start_sequence, ["A", true, Nodes::Sequence::FLOW]
|
||||
end
|
||||
|
||||
def test_sequence_start_tag
|
||||
@parser.parse("---\n!!seq [1, 2]")
|
||||
assert_called :start_sequence, ["tag:yaml.org,2002:seq", false, Nodes::Sequence::FLOW]
|
||||
end
|
||||
|
||||
def test_sequence_start_flow
|
||||
@parser.parse("---\n[1, 2]")
|
||||
assert_called :start_sequence, [true, Nodes::Sequence::FLOW]
|
||||
end
|
||||
|
||||
def test_sequence_start_block
|
||||
@parser.parse("---\n - 1\n - 2")
|
||||
assert_called :start_sequence, [true, Nodes::Sequence::BLOCK]
|
||||
end
|
||||
|
||||
def test_literal_scalar
|
||||
@parser.parse(<<-eoyml)
|
||||
%YAML 1.1
|
||||
---
|
||||
"literal\n\
|
||||
\ttext\n"
|
||||
eoyml
|
||||
assert_called :scalar, ['literal text ', false, true, Nodes::Scalar::DOUBLE_QUOTED]
|
||||
end
|
||||
|
||||
def test_scalar
|
||||
@parser.parse("--- foo\n")
|
||||
assert_called :scalar, ['foo', true, false, Nodes::Scalar::PLAIN]
|
||||
end
|
||||
|
||||
def test_scalar_with_tag
|
||||
@parser.parse("---\n!!str foo\n")
|
||||
assert_called :scalar, ['foo', 'tag:yaml.org,2002:str', false, false, Nodes::Scalar::PLAIN]
|
||||
end
|
||||
|
||||
def test_scalar_with_anchor
|
||||
@parser.parse("---\n&A foo\n")
|
||||
assert_called :scalar, ['foo', 'A', true, false, Nodes::Scalar::PLAIN]
|
||||
end
|
||||
|
||||
def test_scalar_plain_implicit
|
||||
@parser.parse("---\n&A foo\n")
|
||||
assert_called :scalar, ['foo', 'A', true, false, Nodes::Scalar::PLAIN]
|
||||
end
|
||||
|
||||
def test_alias
|
||||
@parser.parse(<<-eoyml)
|
||||
%YAML 1.1
|
||||
---
|
||||
!!seq [
|
||||
!!str "Without properties",
|
||||
&A !!str "Anchored",
|
||||
!!str "Tagged",
|
||||
*A,
|
||||
!!str "",
|
||||
]
|
||||
eoyml
|
||||
assert_called :alias, ['A']
|
||||
end
|
||||
|
||||
def test_end_stream
|
||||
@parser.parse("--- foo\n")
|
||||
assert_called :end_stream
|
||||
end
|
||||
|
||||
def test_start_stream
|
||||
@parser.parse("--- foo\n")
|
||||
assert_called :start_stream
|
||||
end
|
||||
|
||||
def test_end_document_implicit
|
||||
@parser.parse("\"foo\"\n")
|
||||
assert_called :end_document, [true]
|
||||
end
|
||||
|
||||
def test_end_document_explicit
|
||||
@parser.parse("\"foo\"\n...")
|
||||
assert_called :end_document, [false]
|
||||
end
|
||||
|
||||
def test_start_document_version
|
||||
@parser.parse("%YAML 1.1\n---\n\"foo\"\n")
|
||||
assert_called :start_document, [[1,1], [], false]
|
||||
end
|
||||
|
||||
def test_start_document_tag
|
||||
@parser.parse("%TAG !yaml! tag:yaml.org,2002\n---\n!yaml!str \"foo\"\n")
|
||||
assert_called :start_document, [[], [['!yaml!', 'tag:yaml.org,2002']], false]
|
||||
end
|
||||
|
||||
def assert_called call, with = nil, parser = @parser
|
||||
if with
|
||||
call = parser.handler.calls.find { |x|
|
||||
x.first == call && x.last.compact == with
|
||||
}
|
||||
assert(call,
|
||||
"#{[call,with].inspect} not in #{parser.handler.calls.inspect}"
|
||||
)
|
||||
else
|
||||
assert parser.handler.calls.any? { |x| x.first == call }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
69
test/psych/test_psych.rb
Normal file
69
test/psych/test_psych.rb
Normal file
|
@ -0,0 +1,69 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
class TestPsych < Psych::TestCase
|
||||
def test_dump_stream
|
||||
things = [22, "foo \n", {}]
|
||||
stream = Psych.dump_stream(*things)
|
||||
assert_equal things, Psych.load_stream(stream)
|
||||
end
|
||||
|
||||
def test_simple
|
||||
assert_equal 'foo', Psych.load("--- foo\n")
|
||||
end
|
||||
|
||||
def test_libyaml_version
|
||||
assert Psych.libyaml_version
|
||||
assert_equal Psych.libyaml_version.join('.'), Psych::LIBYAML_VERSION
|
||||
end
|
||||
|
||||
def test_load_documents
|
||||
docs = Psych.load_documents("--- foo\n...\n--- bar\n...")
|
||||
assert_equal %w{ foo bar }, docs
|
||||
end
|
||||
|
||||
def test_parse_stream
|
||||
docs = Psych.parse_stream("--- foo\n...\n--- bar\n...")
|
||||
assert_equal %w{ foo bar }, docs.children.map { |x| x.transform }
|
||||
end
|
||||
|
||||
def test_add_builtin_type
|
||||
got = nil
|
||||
Psych.add_builtin_type 'omap', do |type, val|
|
||||
got = val
|
||||
end
|
||||
Psych.load('--- !omap hello')
|
||||
assert_equal 'hello', got
|
||||
ensure
|
||||
Psych.remove_type 'omap'
|
||||
end
|
||||
|
||||
def test_domain_types
|
||||
got = nil
|
||||
Psych.add_domain_type 'foo.bar,2002', 'foo' do |type, val|
|
||||
got = val
|
||||
end
|
||||
|
||||
Psych.load('--- !foo.bar,2002/foo hello')
|
||||
assert_equal 'hello', got
|
||||
|
||||
Psych.load("--- !foo.bar,2002/foo\n- hello\n- world")
|
||||
assert_equal %w{ hello world }, got
|
||||
|
||||
Psych.load("--- !foo.bar,2002/foo\nhello: world")
|
||||
assert_equal({ 'hello' => 'world' }, got)
|
||||
end
|
||||
|
||||
def test_load_file
|
||||
name = File.join(Dir.tmpdir, 'yikes.yml')
|
||||
File.open(name, 'wb') { |f| f.write('--- hello world') }
|
||||
|
||||
assert_equal 'hello world', Psych.load_file(name)
|
||||
end
|
||||
|
||||
def test_parse_file
|
||||
name = File.join(Dir.tmpdir, 'yikes.yml')
|
||||
File.open(name, 'wb') { |f| f.write('--- hello world') }
|
||||
|
||||
assert_equal 'hello world', Psych.parse_file(name).transform
|
||||
end
|
||||
end
|
11
test/psych/test_scalar.rb
Normal file
11
test/psych/test_scalar.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestScalar < TestCase
|
||||
def test_utf_8
|
||||
assert_equal "日本語", Psych.load("--- 日本語")
|
||||
end
|
||||
end
|
||||
end
|
70
test/psych/test_scalar_scanner.rb
Normal file
70
test/psych/test_scalar_scanner.rb
Normal file
|
@ -0,0 +1,70 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestScalarScanner < TestCase
|
||||
def test_scan_time
|
||||
[ '2001-12-15T02:59:43.1Z',
|
||||
'2001-12-14t21:59:43.10-05:00',
|
||||
'2001-12-14 21:59:43.10 -5',
|
||||
'2010-01-06 00:00:00 -08:00',
|
||||
'2001-12-15 2:59:43.10',
|
||||
].each do |time|
|
||||
ss = Psych::ScalarScanner.new
|
||||
assert_instance_of Time, ss.tokenize(time)
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :ss
|
||||
|
||||
def setup
|
||||
super
|
||||
@ss = Psych::ScalarScanner.new
|
||||
end
|
||||
|
||||
def test_scan_date
|
||||
date = '1980-12-16'
|
||||
token = @ss.tokenize date
|
||||
assert_equal 1980, token.year
|
||||
assert_equal 12, token.month
|
||||
assert_equal 16, token.day
|
||||
end
|
||||
|
||||
def test_scan_inf
|
||||
assert_equal(1 / 0.0, ss.tokenize('.inf'))
|
||||
end
|
||||
|
||||
def test_scan_minus_inf
|
||||
assert_equal(-1 / 0.0, ss.tokenize('-.inf'))
|
||||
end
|
||||
|
||||
def test_scan_nan
|
||||
assert ss.tokenize('.nan').nan?
|
||||
end
|
||||
|
||||
def test_scan_null
|
||||
assert_equal nil, ss.tokenize('null')
|
||||
assert_equal nil, ss.tokenize('~')
|
||||
assert_equal nil, ss.tokenize('')
|
||||
end
|
||||
|
||||
def test_scan_symbol
|
||||
assert_equal :foo, ss.tokenize(':foo')
|
||||
end
|
||||
|
||||
def test_scan_sexagesimal_float
|
||||
assert_equal 685230.15, ss.tokenize('190:20:30.15')
|
||||
end
|
||||
|
||||
def test_scan_sexagesimal_int
|
||||
assert_equal 685230, ss.tokenize('190:20:30')
|
||||
end
|
||||
|
||||
def test_scan_float
|
||||
assert_equal 1.2, ss.tokenize('1.2')
|
||||
end
|
||||
|
||||
def test_scan_true
|
||||
assert_equal true, ss.tokenize('true')
|
||||
end
|
||||
end
|
||||
end
|
38
test/psych/test_serialize_subclasses.rb
Normal file
38
test/psych/test_serialize_subclasses.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestSerializeSubclasses < TestCase
|
||||
class SomeObject
|
||||
def initialize one, two
|
||||
@one = one
|
||||
@two = two
|
||||
end
|
||||
|
||||
def == other
|
||||
@one == other.instance_eval { @one } &&
|
||||
@two == other.instance_eval { @two }
|
||||
end
|
||||
end
|
||||
|
||||
def test_some_object
|
||||
so = SomeObject.new('foo', [1,2,3])
|
||||
assert_equal so, Psych.load(Psych.dump(so))
|
||||
end
|
||||
|
||||
class StructSubclass < Struct.new(:foo)
|
||||
def initialize foo, bar
|
||||
super(foo)
|
||||
@bar = bar
|
||||
end
|
||||
|
||||
def == other
|
||||
super(other) && @bar == other.instance_eval{ @bar }
|
||||
end
|
||||
end
|
||||
|
||||
def test_struct_subclass
|
||||
so = StructSubclass.new('foo', [1,2,3])
|
||||
assert_equal so, Psych.load(Psych.dump(so))
|
||||
end
|
||||
end
|
||||
end
|
49
test/psych/test_set.rb
Normal file
49
test/psych/test_set.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestSet < TestCase
|
||||
def setup
|
||||
super
|
||||
@set = Psych::Set.new
|
||||
@set['foo'] = 'bar'
|
||||
@set['bar'] = 'baz'
|
||||
end
|
||||
|
||||
def test_dump
|
||||
assert_match(/!set/, Psych.dump(@set))
|
||||
end
|
||||
|
||||
def test_roundtrip
|
||||
assert_cycle(@set)
|
||||
end
|
||||
|
||||
###
|
||||
# FIXME: Syck should also support !!set as shorthand
|
||||
def test_load_from_yaml
|
||||
loaded = Psych.load(<<-eoyml)
|
||||
--- !set
|
||||
foo: bar
|
||||
bar: baz
|
||||
eoyml
|
||||
assert_equal(@set, loaded)
|
||||
end
|
||||
|
||||
def test_loaded_class
|
||||
assert_instance_of(Psych::Set, Psych.load(Psych.dump(@set)))
|
||||
end
|
||||
|
||||
def test_set_shorthand
|
||||
loaded = Psych.load(<<-eoyml)
|
||||
--- !!set
|
||||
foo: bar
|
||||
bar: baz
|
||||
eoyml
|
||||
assert_instance_of(Psych::Set, loaded)
|
||||
end
|
||||
|
||||
def test_set_self_reference
|
||||
@set['self'] = @set
|
||||
assert_cycle(@set)
|
||||
end
|
||||
end
|
||||
end
|
49
test/psych/test_string.rb
Normal file
49
test/psych/test_string.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestString < TestCase
|
||||
def test_binary_string_null
|
||||
string = "\x00"
|
||||
yml = Psych.dump string
|
||||
assert_match(/binary/, yml)
|
||||
assert_equal string, Psych.load(yml)
|
||||
end
|
||||
|
||||
def test_binary_string
|
||||
string = binary_string
|
||||
yml = Psych.dump string
|
||||
assert_match(/binary/, yml)
|
||||
assert_equal string, Psych.load(yml)
|
||||
end
|
||||
|
||||
def test_non_binary_string
|
||||
string = binary_string(0.29)
|
||||
yml = Psych.dump string
|
||||
refute_match(/binary/, yml)
|
||||
assert_equal string, Psych.load(yml)
|
||||
end
|
||||
|
||||
def test_string_with_ivars
|
||||
food = "is delicious"
|
||||
ivar = "on rock and roll"
|
||||
food.instance_variable_set(:@we_built_this_city, ivar)
|
||||
|
||||
str = Psych.load Psych.dump food
|
||||
assert_equal ivar, food.instance_variable_get(:@we_built_this_city)
|
||||
end
|
||||
|
||||
def test_binary
|
||||
string = [0, 123,22, 44, 9, 32, 34, 39].pack('C*')
|
||||
assert_cycle string
|
||||
end
|
||||
|
||||
def binary_string percentage = 0.31, length = 100
|
||||
string = ''
|
||||
(percentage * length).to_i.times do |i|
|
||||
string << "\b"
|
||||
end
|
||||
string << 'a' * (length - string.length)
|
||||
string
|
||||
end
|
||||
end
|
||||
end
|
51
test/psych/test_struct.rb
Normal file
51
test/psych/test_struct.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
class PsychStructWithIvar < Struct.new(:foo)
|
||||
attr_reader :bar
|
||||
def initialize *args
|
||||
super
|
||||
@bar = 'hello'
|
||||
end
|
||||
end
|
||||
|
||||
module Psych
|
||||
class TestStruct < TestCase
|
||||
class StructSubclass < Struct.new(:foo)
|
||||
def initialize foo, bar
|
||||
super(foo)
|
||||
@bar = bar
|
||||
end
|
||||
end
|
||||
|
||||
def test_self_referential_struct
|
||||
ss = StructSubclass.new(nil, 'foo')
|
||||
ss.foo = ss
|
||||
|
||||
loaded = Psych.load(Psych.dump(ss))
|
||||
assert_instance_of(StructSubclass, loaded.foo)
|
||||
|
||||
# FIXME: This seems to cause an infinite loop. wtf. Must report a bug
|
||||
# in ruby.
|
||||
# assert_equal(ss, loaded)
|
||||
end
|
||||
|
||||
def test_roundtrip
|
||||
thing = PsychStructWithIvar.new('bar')
|
||||
struct = Psych.load(Psych.dump(thing))
|
||||
|
||||
assert_equal 'hello', struct.bar
|
||||
assert_equal 'bar', struct.foo
|
||||
end
|
||||
|
||||
def test_load
|
||||
obj = Psych.load(<<-eoyml)
|
||||
--- !ruby/struct:PsychStructWithIvar
|
||||
:foo: bar
|
||||
:@bar: hello
|
||||
eoyml
|
||||
|
||||
assert_equal 'hello', obj.bar
|
||||
assert_equal 'bar', obj.foo
|
||||
end
|
||||
end
|
||||
end
|
17
test/psych/test_symbol.rb
Normal file
17
test/psych/test_symbol.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestSymbol < TestCase
|
||||
def test_cycle
|
||||
assert_cycle :a
|
||||
end
|
||||
|
||||
def test_stringy
|
||||
assert_cycle :"1"
|
||||
end
|
||||
|
||||
def test_load_quoted
|
||||
assert_equal :"1", Psych.load("--- :'1'\n")
|
||||
end
|
||||
end
|
||||
end
|
63
test/psych/test_to_yaml_properties.rb
Normal file
63
test/psych/test_to_yaml_properties.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestToYamlProperties < MiniTest::Unit::TestCase
|
||||
class Foo
|
||||
attr_accessor :a, :b, :c
|
||||
def initialize
|
||||
@a = 1
|
||||
@b = 2
|
||||
@c = 3
|
||||
end
|
||||
|
||||
def to_yaml_properties
|
||||
[:@a, :@b]
|
||||
end
|
||||
end
|
||||
|
||||
def test_object_dump_yaml_properties
|
||||
foo = Psych.load(Psych.dump(Foo.new))
|
||||
assert_equal 1, foo.a
|
||||
assert_equal 2, foo.b
|
||||
assert_nil foo.c
|
||||
end
|
||||
|
||||
class Bar < Struct.new(:foo, :bar)
|
||||
attr_reader :baz
|
||||
def initialize *args
|
||||
super
|
||||
@baz = 'hello'
|
||||
end
|
||||
|
||||
def to_yaml_properties
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def test_struct_dump_yaml_properties
|
||||
bar = Psych.load(Psych.dump(Bar.new('a', 'b')))
|
||||
assert_equal 'a', bar.foo
|
||||
assert_equal 'b', bar.bar
|
||||
assert_nil bar.baz
|
||||
end
|
||||
|
||||
def test_string_dump
|
||||
string = "okonomiyaki"
|
||||
class << string
|
||||
def to_yaml_properties
|
||||
[:@tastes]
|
||||
end
|
||||
end
|
||||
|
||||
string.instance_variable_set(:@tastes, 'delicious')
|
||||
v = Psych.load Psych.dump string
|
||||
assert_equal 'delicious', v.instance_variable_get(:@tastes)
|
||||
end
|
||||
|
||||
def test_string_load_syck
|
||||
str = Psych.load("--- !str \nstr: okonomiyaki\n:@tastes: delicious\n")
|
||||
assert_equal 'okonomiyaki', str
|
||||
assert_equal 'delicious', str.instance_variable_get(:@tastes)
|
||||
end
|
||||
end
|
||||
end
|
79
test/psych/test_tree_builder.rb
Normal file
79
test/psych/test_tree_builder.rb
Normal file
|
@ -0,0 +1,79 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
class TestTreeBuilder < TestCase
|
||||
def setup
|
||||
super
|
||||
@parser = Psych::Parser.new TreeBuilder.new
|
||||
@parser.parse(<<-eoyml)
|
||||
%YAML 1.1
|
||||
---
|
||||
- foo
|
||||
- {
|
||||
bar : &A !!str baz,
|
||||
boo : *A
|
||||
}
|
||||
- *A
|
||||
eoyml
|
||||
@tree = @parser.handler.root
|
||||
end
|
||||
|
||||
def test_stream
|
||||
assert_instance_of Nodes::Stream, @tree
|
||||
end
|
||||
|
||||
def test_documents
|
||||
assert_equal 1, @tree.children.length
|
||||
assert_instance_of Nodes::Document, @tree.children.first
|
||||
doc = @tree.children.first
|
||||
|
||||
assert_equal [1,1], doc.version
|
||||
assert_equal [], doc.tag_directives
|
||||
assert_equal false, doc.implicit
|
||||
end
|
||||
|
||||
def test_sequence
|
||||
doc = @tree.children.first
|
||||
assert_equal 1, doc.children.length
|
||||
|
||||
seq = doc.children.first
|
||||
assert_instance_of Nodes::Sequence, seq
|
||||
assert_nil seq.anchor
|
||||
assert_nil seq.tag
|
||||
assert_equal true, seq.implicit
|
||||
assert_equal Nodes::Sequence::BLOCK, seq.style
|
||||
end
|
||||
|
||||
def test_scalar
|
||||
doc = @tree.children.first
|
||||
seq = doc.children.first
|
||||
|
||||
assert_equal 3, seq.children.length
|
||||
scalar = seq.children.first
|
||||
assert_instance_of Nodes::Scalar, scalar
|
||||
assert_equal 'foo', scalar.value
|
||||
assert_nil scalar.anchor
|
||||
assert_nil scalar.tag
|
||||
assert_equal true, scalar.plain
|
||||
assert_equal false, scalar.quoted
|
||||
assert_equal Nodes::Scalar::PLAIN, scalar.style
|
||||
end
|
||||
|
||||
def test_mapping
|
||||
doc = @tree.children.first
|
||||
seq = doc.children.first
|
||||
map = seq.children[1]
|
||||
|
||||
assert_instance_of Nodes::Mapping, map
|
||||
end
|
||||
|
||||
def test_alias
|
||||
doc = @tree.children.first
|
||||
seq = doc.children.first
|
||||
assert_equal 3, seq.children.length
|
||||
al = seq.children[2]
|
||||
assert_instance_of Nodes::Alias, al
|
||||
assert_equal 'A', al.anchor
|
||||
end
|
||||
end
|
||||
end
|
1248
test/psych/test_yaml.rb
Normal file
1248
test/psych/test_yaml.rb
Normal file
File diff suppressed because it is too large
Load diff
124
test/psych/visitors/test_emitter.rb
Normal file
124
test/psych/visitors/test_emitter.rb
Normal file
|
@ -0,0 +1,124 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
module Visitors
|
||||
class TestEmitter < TestCase
|
||||
def setup
|
||||
super
|
||||
@io = StringIO.new
|
||||
@visitor = Visitors::Emitter.new @io
|
||||
end
|
||||
|
||||
def test_stream
|
||||
s = Nodes::Stream.new
|
||||
@visitor.accept s
|
||||
assert_equal '', @io.string
|
||||
end
|
||||
|
||||
def test_document
|
||||
s = Nodes::Stream.new
|
||||
doc = Nodes::Document.new [1,1]
|
||||
scalar = Nodes::Scalar.new 'hello world'
|
||||
|
||||
doc.children << scalar
|
||||
s.children << doc
|
||||
|
||||
@visitor.accept s
|
||||
|
||||
assert_match(/1.1/, @io.string)
|
||||
assert_equal @io.string, s.to_yaml
|
||||
end
|
||||
|
||||
def test_document_implicit_end
|
||||
s = Nodes::Stream.new
|
||||
doc = Nodes::Document.new
|
||||
mapping = Nodes::Mapping.new
|
||||
mapping.children << Nodes::Scalar.new('key')
|
||||
mapping.children << Nodes::Scalar.new('value')
|
||||
doc.children << mapping
|
||||
s.children << doc
|
||||
|
||||
@visitor.accept s
|
||||
|
||||
assert_match(/key: value/, @io.string)
|
||||
assert_equal @io.string, s.to_yaml
|
||||
assert(/\.\.\./ !~ s.to_yaml)
|
||||
end
|
||||
|
||||
def test_scalar
|
||||
s = Nodes::Stream.new
|
||||
doc = Nodes::Document.new
|
||||
scalar = Nodes::Scalar.new 'hello world'
|
||||
|
||||
doc.children << scalar
|
||||
s.children << doc
|
||||
|
||||
@visitor.accept s
|
||||
|
||||
assert_match(/hello/, @io.string)
|
||||
assert_equal @io.string, s.to_yaml
|
||||
end
|
||||
|
||||
def test_scalar_with_tag
|
||||
s = Nodes::Stream.new
|
||||
doc = Nodes::Document.new
|
||||
scalar = Nodes::Scalar.new 'hello world', nil, '!str', false, false, 5
|
||||
|
||||
doc.children << scalar
|
||||
s.children << doc
|
||||
|
||||
@visitor.accept s
|
||||
|
||||
assert_match(/str/, @io.string)
|
||||
assert_match(/hello/, @io.string)
|
||||
assert_equal @io.string, s.to_yaml
|
||||
end
|
||||
|
||||
def test_sequence
|
||||
s = Nodes::Stream.new
|
||||
doc = Nodes::Document.new
|
||||
scalar = Nodes::Scalar.new 'hello world'
|
||||
seq = Nodes::Sequence.new
|
||||
|
||||
seq.children << scalar
|
||||
doc.children << seq
|
||||
s.children << doc
|
||||
|
||||
@visitor.accept s
|
||||
|
||||
assert_match(/- hello/, @io.string)
|
||||
assert_equal @io.string, s.to_yaml
|
||||
end
|
||||
|
||||
def test_mapping
|
||||
s = Nodes::Stream.new
|
||||
doc = Nodes::Document.new
|
||||
mapping = Nodes::Mapping.new
|
||||
mapping.children << Nodes::Scalar.new('key')
|
||||
mapping.children << Nodes::Scalar.new('value')
|
||||
doc.children << mapping
|
||||
s.children << doc
|
||||
|
||||
@visitor.accept s
|
||||
|
||||
assert_match(/key: value/, @io.string)
|
||||
assert_equal @io.string, s.to_yaml
|
||||
end
|
||||
|
||||
def test_alias
|
||||
s = Nodes::Stream.new
|
||||
doc = Nodes::Document.new
|
||||
mapping = Nodes::Mapping.new
|
||||
mapping.children << Nodes::Scalar.new('key', 'A')
|
||||
mapping.children << Nodes::Alias.new('A')
|
||||
doc.children << mapping
|
||||
s.children << doc
|
||||
|
||||
@visitor.accept s
|
||||
|
||||
assert_match(/&A key: \*A/, @io.string)
|
||||
assert_equal @io.string, s.to_yaml
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
323
test/psych/visitors/test_to_ruby.rb
Normal file
323
test/psych/visitors/test_to_ruby.rb
Normal file
|
@ -0,0 +1,323 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
module Visitors
|
||||
class TestToRuby < TestCase
|
||||
def setup
|
||||
super
|
||||
@visitor = ToRuby.new
|
||||
end
|
||||
|
||||
def test_object
|
||||
mapping = Nodes::Mapping.new nil, "!ruby/object"
|
||||
mapping.children << Nodes::Scalar.new('foo')
|
||||
mapping.children << Nodes::Scalar.new('bar')
|
||||
|
||||
o = mapping.to_ruby
|
||||
assert_equal 'bar', o.instance_variable_get(:@foo)
|
||||
end
|
||||
|
||||
def test_awesome
|
||||
Psych.load('1900-01-01T00:00:00+00:00')
|
||||
end
|
||||
|
||||
def test_legacy_struct
|
||||
foo = Struct.new('AWESOME', :bar)
|
||||
assert_equal foo.new('baz'), Psych.load(<<-eoyml)
|
||||
!ruby/struct:AWESOME
|
||||
bar: baz
|
||||
eoyml
|
||||
end
|
||||
|
||||
def test_binary
|
||||
gif = "GIF89a\f\x00\f\x00\x84\x00\x00\xFF\xFF\xF7\xF5\xF5\xEE\xE9\xE9\xE5fff\x00\x00\x00\xE7\xE7\xE7^^^\xF3\xF3\xED\x8E\x8E\x8E\xE0\xE0\xE0\x9F\x9F\x9F\x93\x93\x93\xA7\xA7\xA7\x9E\x9E\x9Eiiiccc\xA3\xA3\xA3\x84\x84\x84\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9!\xFE\x0EMade with GIMP\x00,\x00\x00\x00\x00\f\x00\f\x00\x00\x05, \x8E\x810\x9E\xE3@\x14\xE8i\x10\xC4\xD1\x8A\b\x1C\xCF\x80M$z\xEF\xFF0\x85p\xB8\xB01f\r\e\xCE\x01\xC3\x01\x1E\x10' \x82\n\x01\x00;"
|
||||
|
||||
hash = Psych.load(<<-'eoyaml')
|
||||
canonical: !!binary "\
|
||||
R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\
|
||||
OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\
|
||||
+f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\
|
||||
AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs="
|
||||
generic: !binary |
|
||||
R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
|
||||
OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
|
||||
+f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
|
||||
AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=
|
||||
description:
|
||||
The binary value above is a tiny arrow encoded as a gif image.
|
||||
eoyaml
|
||||
assert_equal gif, hash['canonical']
|
||||
assert_equal gif, hash['generic']
|
||||
end
|
||||
|
||||
A = Struct.new(:foo)
|
||||
|
||||
def test_struct
|
||||
s = A.new('bar')
|
||||
|
||||
mapping = Nodes::Mapping.new nil, "!ruby/struct:#{s.class}"
|
||||
mapping.children << Nodes::Scalar.new('foo')
|
||||
mapping.children << Nodes::Scalar.new('bar')
|
||||
|
||||
ruby = mapping.to_ruby
|
||||
|
||||
assert_equal s.class, ruby.class
|
||||
assert_equal s.foo, ruby.foo
|
||||
assert_equal s, ruby
|
||||
end
|
||||
|
||||
def test_anon_struct_legacy
|
||||
s = Struct.new(:foo).new('bar')
|
||||
|
||||
mapping = Nodes::Mapping.new nil, '!ruby/struct:'
|
||||
mapping.children << Nodes::Scalar.new('foo')
|
||||
mapping.children << Nodes::Scalar.new('bar')
|
||||
|
||||
assert_equal s.foo, mapping.to_ruby.foo
|
||||
end
|
||||
|
||||
def test_anon_struct
|
||||
s = Struct.new(:foo).new('bar')
|
||||
|
||||
mapping = Nodes::Mapping.new nil, '!ruby/struct'
|
||||
mapping.children << Nodes::Scalar.new('foo')
|
||||
mapping.children << Nodes::Scalar.new('bar')
|
||||
|
||||
assert_equal s.foo, mapping.to_ruby.foo
|
||||
end
|
||||
|
||||
def test_exception
|
||||
exc = Exception.new 'hello'
|
||||
|
||||
mapping = Nodes::Mapping.new nil, '!ruby/exception'
|
||||
mapping.children << Nodes::Scalar.new('message')
|
||||
mapping.children << Nodes::Scalar.new('hello')
|
||||
|
||||
ruby = mapping.to_ruby
|
||||
|
||||
assert_equal exc.class, ruby.class
|
||||
assert_equal exc.message, ruby.message
|
||||
end
|
||||
|
||||
def test_regexp
|
||||
node = Nodes::Scalar.new('/foo/', nil, '!ruby/regexp')
|
||||
assert_equal(/foo/, node.to_ruby)
|
||||
|
||||
node = Nodes::Scalar.new('/foo/m', nil, '!ruby/regexp')
|
||||
assert_equal(/foo/m, node.to_ruby)
|
||||
|
||||
node = Nodes::Scalar.new('/foo/ix', nil, '!ruby/regexp')
|
||||
assert_equal(/foo/ix, node.to_ruby)
|
||||
end
|
||||
|
||||
def test_time
|
||||
now = Time.now
|
||||
formatted = now.strftime("%Y-%m-%d %H:%M:%S") +
|
||||
".%06d %+.2d:00" % [now.usec, now.gmt_offset / 3600]
|
||||
|
||||
assert_equal now, Nodes::Scalar.new(formatted).to_ruby
|
||||
end
|
||||
|
||||
def test_time_utc
|
||||
now = Time.now.utc
|
||||
formatted = now.strftime("%Y-%m-%d %H:%M:%S") +
|
||||
".%06dZ" % [now.usec]
|
||||
|
||||
assert_equal now, Nodes::Scalar.new(formatted).to_ruby
|
||||
end
|
||||
|
||||
def test_time_utc_no_z
|
||||
now = Time.now.utc
|
||||
formatted = now.strftime("%Y-%m-%d %H:%M:%S") +
|
||||
".%06d" % [now.usec]
|
||||
|
||||
assert_equal now, Nodes::Scalar.new(formatted).to_ruby
|
||||
end
|
||||
|
||||
def test_date
|
||||
d = '1980-12-16'
|
||||
actual = Date.strptime(d, '%Y-%m-%d')
|
||||
|
||||
date = Nodes::Scalar.new(d, nil, 'tag:yaml.org,2002:timestamp', false)
|
||||
|
||||
assert_equal actual, date.to_ruby
|
||||
end
|
||||
|
||||
def test_rational
|
||||
mapping = Nodes::Mapping.new nil, '!ruby/object:Rational'
|
||||
mapping.children << Nodes::Scalar.new('denominator')
|
||||
mapping.children << Nodes::Scalar.new('2')
|
||||
mapping.children << Nodes::Scalar.new('numerator')
|
||||
mapping.children << Nodes::Scalar.new('1')
|
||||
|
||||
assert_equal Rational(1,2), mapping.to_ruby
|
||||
end
|
||||
|
||||
def test_complex
|
||||
mapping = Nodes::Mapping.new nil, '!ruby/object:Complex'
|
||||
mapping.children << Nodes::Scalar.new('image')
|
||||
mapping.children << Nodes::Scalar.new('2')
|
||||
mapping.children << Nodes::Scalar.new('real')
|
||||
mapping.children << Nodes::Scalar.new('1')
|
||||
|
||||
assert_equal Complex(1,2), mapping.to_ruby
|
||||
end
|
||||
|
||||
if RUBY_VERSION >= '1.9'
|
||||
def test_complex_string
|
||||
node = Nodes::Scalar.new '3+4i', nil, "!ruby/object:Complex"
|
||||
assert_equal Complex(3, 4), node.to_ruby
|
||||
end
|
||||
|
||||
def test_rational_string
|
||||
node = Nodes::Scalar.new '1/2', nil, "!ruby/object:Rational"
|
||||
assert_equal Rational(1, 2), node.to_ruby
|
||||
end
|
||||
end
|
||||
|
||||
def test_range_string
|
||||
node = Nodes::Scalar.new '1..2', nil, "!ruby/range"
|
||||
assert_equal 1..2, node.to_ruby
|
||||
end
|
||||
|
||||
def test_range_string_triple
|
||||
node = Nodes::Scalar.new '1...3', nil, "!ruby/range"
|
||||
assert_equal 1...3, node.to_ruby
|
||||
end
|
||||
|
||||
def test_integer
|
||||
i = Nodes::Scalar.new('1', nil, 'tag:yaml.org,2002:int')
|
||||
assert_equal 1, i.to_ruby
|
||||
|
||||
assert_equal 1, Nodes::Scalar.new('1').to_ruby
|
||||
|
||||
i = Nodes::Scalar.new('-1', nil, 'tag:yaml.org,2002:int')
|
||||
assert_equal(-1, i.to_ruby)
|
||||
|
||||
assert_equal(-1, Nodes::Scalar.new('-1').to_ruby)
|
||||
assert_equal 1, Nodes::Scalar.new('+1').to_ruby
|
||||
end
|
||||
|
||||
def test_int_ignore
|
||||
['1,000', '1_000'].each do |num|
|
||||
i = Nodes::Scalar.new(num, nil, 'tag:yaml.org,2002:int')
|
||||
assert_equal 1000, i.to_ruby
|
||||
|
||||
assert_equal 1000, Nodes::Scalar.new(num).to_ruby
|
||||
end
|
||||
end
|
||||
|
||||
def test_float_ignore
|
||||
['1,000.3', '1_000.3'].each do |num|
|
||||
i = Nodes::Scalar.new(num, nil, 'tag:yaml.org,2002:float')
|
||||
assert_equal 1000.3, i.to_ruby
|
||||
|
||||
i = Nodes::Scalar.new(num, nil, '!float')
|
||||
assert_equal 1000.3, i.to_ruby
|
||||
|
||||
assert_equal 1000.3, Nodes::Scalar.new(num).to_ruby
|
||||
end
|
||||
end
|
||||
|
||||
# http://yaml.org/type/bool.html
|
||||
def test_boolean_true
|
||||
%w{ yes Yes YES true True TRUE on On ON }.each do |t|
|
||||
i = Nodes::Scalar.new(t, nil, 'tag:yaml.org,2002:bool')
|
||||
assert_equal true, i.to_ruby
|
||||
assert_equal true, Nodes::Scalar.new(t).to_ruby
|
||||
end
|
||||
end
|
||||
|
||||
# http://yaml.org/type/bool.html
|
||||
def test_boolean_false
|
||||
%w{ no No NO false False FALSE off Off OFF }.each do |t|
|
||||
i = Nodes::Scalar.new(t, nil, 'tag:yaml.org,2002:bool')
|
||||
assert_equal false, i.to_ruby
|
||||
assert_equal false, Nodes::Scalar.new(t).to_ruby
|
||||
end
|
||||
end
|
||||
|
||||
def test_float
|
||||
i = Nodes::Scalar.new('12', nil, 'tag:yaml.org,2002:float')
|
||||
assert_equal 12.0, i.to_ruby
|
||||
|
||||
i = Nodes::Scalar.new('1.2', nil, 'tag:yaml.org,2002:float')
|
||||
assert_equal 1.2, i.to_ruby
|
||||
|
||||
i = Nodes::Scalar.new('1.2')
|
||||
assert_equal 1.2, i.to_ruby
|
||||
|
||||
assert_equal 1, Nodes::Scalar.new('.Inf').to_ruby.infinite?
|
||||
assert_equal 1, Nodes::Scalar.new('.inf').to_ruby.infinite?
|
||||
assert_equal 1, Nodes::Scalar.new('.Inf', nil, 'tag:yaml.org,2002:float').to_ruby.infinite?
|
||||
|
||||
assert_equal(-1, Nodes::Scalar.new('-.inf').to_ruby.infinite?)
|
||||
assert_equal(-1, Nodes::Scalar.new('-.Inf').to_ruby.infinite?)
|
||||
assert_equal(-1, Nodes::Scalar.new('-.Inf', nil, 'tag:yaml.org,2002:float').to_ruby.infinite?)
|
||||
|
||||
assert Nodes::Scalar.new('.NaN').to_ruby.nan?
|
||||
assert Nodes::Scalar.new('.NaN', nil, 'tag:yaml.org,2002:float').to_ruby.nan?
|
||||
end
|
||||
|
||||
def test_exp_float
|
||||
exp = 1.2e+30
|
||||
|
||||
i = Nodes::Scalar.new(exp.to_s, nil, 'tag:yaml.org,2002:float')
|
||||
assert_equal exp, i.to_ruby
|
||||
|
||||
assert_equal exp, Nodes::Scalar.new(exp.to_s).to_ruby
|
||||
end
|
||||
|
||||
def test_scalar
|
||||
scalar = Nodes::Scalar.new('foo')
|
||||
assert_equal 'foo', @visitor.accept(scalar)
|
||||
assert_equal 'foo', scalar.to_ruby
|
||||
end
|
||||
|
||||
def test_sequence
|
||||
seq = Nodes::Sequence.new
|
||||
seq.children << Nodes::Scalar.new('foo')
|
||||
seq.children << Nodes::Scalar.new('bar')
|
||||
|
||||
assert_equal %w{ foo bar }, seq.to_ruby
|
||||
end
|
||||
|
||||
def test_mapping
|
||||
mapping = Nodes::Mapping.new
|
||||
mapping.children << Nodes::Scalar.new('foo')
|
||||
mapping.children << Nodes::Scalar.new('bar')
|
||||
assert_equal({'foo' => 'bar'}, mapping.to_ruby)
|
||||
end
|
||||
|
||||
def test_document
|
||||
doc = Nodes::Document.new
|
||||
doc.children << Nodes::Scalar.new('foo')
|
||||
assert_equal 'foo', doc.to_ruby
|
||||
end
|
||||
|
||||
def test_stream
|
||||
a = Nodes::Document.new
|
||||
a.children << Nodes::Scalar.new('foo')
|
||||
|
||||
b = Nodes::Document.new
|
||||
b.children << Nodes::Scalar.new('bar')
|
||||
|
||||
stream = Nodes::Stream.new
|
||||
stream.children << a
|
||||
stream.children << b
|
||||
|
||||
assert_equal %w{ foo bar }, stream.to_ruby
|
||||
end
|
||||
|
||||
def test_alias
|
||||
seq = Nodes::Sequence.new
|
||||
seq.children << Nodes::Scalar.new('foo', 'A')
|
||||
seq.children << Nodes::Alias.new('A')
|
||||
|
||||
list = seq.to_ruby
|
||||
assert_equal %w{ foo foo }, list
|
||||
assert_equal list[0].object_id, list[1].object_id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
140
test/psych/visitors/test_yaml_tree.rb
Normal file
140
test/psych/visitors/test_yaml_tree.rb
Normal file
|
@ -0,0 +1,140 @@
|
|||
require 'test/psych/helper'
|
||||
|
||||
module Psych
|
||||
module Visitors
|
||||
class TestYAMLTree < TestCase
|
||||
def setup
|
||||
super
|
||||
@v = Visitors::YAMLTree.new
|
||||
end
|
||||
|
||||
def test_object_has_no_class
|
||||
yaml = Psych.dump(Object.new)
|
||||
assert(Psych.dump(Object.new) !~ /Object/, yaml)
|
||||
end
|
||||
|
||||
def test_struct_const
|
||||
foo = Struct.new("Foo", :bar)
|
||||
assert_cycle foo.new('bar')
|
||||
end
|
||||
|
||||
A = Struct.new(:foo)
|
||||
|
||||
def test_struct
|
||||
assert_cycle A.new('bar')
|
||||
end
|
||||
|
||||
def test_struct_anon
|
||||
s = Struct.new(:foo).new('bar')
|
||||
obj = Psych.load(Psych.dump(s))
|
||||
assert_equal s.foo, obj.foo
|
||||
end
|
||||
|
||||
def test_exception
|
||||
ex = Exception.new 'foo'
|
||||
loaded = Psych.load(Psych.dump(ex))
|
||||
|
||||
assert_equal ex.message, loaded.message
|
||||
assert_equal ex.class, loaded.class
|
||||
end
|
||||
|
||||
def test_regexp
|
||||
assert_cycle(/foo/)
|
||||
assert_cycle(/foo/i)
|
||||
assert_cycle(/foo/mx)
|
||||
end
|
||||
|
||||
def test_time
|
||||
assert_cycle Time.now
|
||||
end
|
||||
|
||||
def test_date
|
||||
date = Date.strptime('2002-12-14', '%Y-%m-%d')
|
||||
assert_cycle date
|
||||
end
|
||||
|
||||
def test_rational
|
||||
assert_cycle Rational(1,2)
|
||||
end
|
||||
|
||||
def test_complex
|
||||
assert_cycle Complex(1,2)
|
||||
end
|
||||
|
||||
def test_scalar
|
||||
assert_cycle 'foo'
|
||||
assert_cycle ':foo'
|
||||
assert_cycle ''
|
||||
assert_cycle ':'
|
||||
end
|
||||
|
||||
def test_boolean
|
||||
assert_cycle true
|
||||
assert_cycle 'true'
|
||||
assert_cycle false
|
||||
assert_cycle 'false'
|
||||
end
|
||||
|
||||
def test_range_inclusive
|
||||
assert_cycle 1..2
|
||||
end
|
||||
|
||||
def test_range_exclusive
|
||||
assert_cycle 1...2
|
||||
end
|
||||
|
||||
def test_anon_class
|
||||
assert_raises(TypeError) do
|
||||
@v.accept Class.new
|
||||
end
|
||||
|
||||
assert_raises(TypeError) do
|
||||
Psych.dump(Class.new)
|
||||
end
|
||||
end
|
||||
|
||||
def test_hash
|
||||
assert_cycle('a' => 'b')
|
||||
end
|
||||
|
||||
def test_list
|
||||
assert_cycle(%w{ a b })
|
||||
assert_cycle([1, 2.2])
|
||||
end
|
||||
|
||||
def test_symbol
|
||||
assert_cycle :foo
|
||||
end
|
||||
|
||||
def test_int
|
||||
assert_cycle 1
|
||||
assert_cycle(-1)
|
||||
assert_cycle '1'
|
||||
assert_cycle '-1'
|
||||
end
|
||||
|
||||
def test_float
|
||||
assert_cycle 1.2
|
||||
assert_cycle '1.2'
|
||||
|
||||
assert Psych.load(Psych.dump(0.0 / 0.0)).nan?
|
||||
assert_equal 1, Psych.load(Psych.dump(1 / 0.0)).infinite?
|
||||
assert_equal(-1, Psych.load(Psych.dump(-1 / 0.0)).infinite?)
|
||||
end
|
||||
|
||||
# http://yaml.org/type/null.html
|
||||
def test_nil
|
||||
assert_cycle nil
|
||||
assert_equal nil, Psych.load('null')
|
||||
assert_equal nil, Psych.load('Null')
|
||||
assert_equal nil, Psych.load('NULL')
|
||||
assert_equal nil, Psych.load('~')
|
||||
assert_equal({'foo' => nil}, Psych.load('foo: '))
|
||||
|
||||
assert_cycle 'null'
|
||||
assert_cycle 'nUll'
|
||||
assert_cycle '~'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue