1
0
Fork 0
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:
tenderlove 2010-03-28 21:49:37 +00:00
parent a8a99a7379
commit b9b923ca94
66 changed files with 6056 additions and 0 deletions

View file

@ -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> Sun Mar 28 21:04:21 2010 NARUSE, Yui <naruse@ruby-lang.org>
* configure.in (ac_cv_func_snprintf, ac_cv_func_vsnprintf): * configure.in (ac_cv_func_snprintf, ac_cv_func_vsnprintf):

413
ext/psych/emitter.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,4 @@
module Psych
class Emitter < Psych::Handler
end
end

215
lib/psych/handler.rb Normal file
View 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
View 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
View 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

View 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

View 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
View 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
View 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

View 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
View 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
View file

@ -0,0 +1,4 @@
module Psych
class Omap < ::Hash
end
end

44
lib/psych/parser.rb Normal file
View 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

View 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
View file

@ -0,0 +1,4 @@
module Psych
class Set < ::Hash
end
end

89
lib/psych/tree_builder.rb Normal file
View 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
View 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'

View 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

View 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

View 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

View 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

View 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
View 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

View 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
View 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

View 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
View 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
View 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

View 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

View 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

View 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

View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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
View 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
View 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
View 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
View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

View 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