json-ld 3.0.2 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHORS +1 -1
  3. data/README.md +90 -53
  4. data/UNLICENSE +1 -1
  5. data/VERSION +1 -1
  6. data/bin/jsonld +4 -4
  7. data/lib/json/ld.rb +27 -10
  8. data/lib/json/ld/api.rb +325 -96
  9. data/lib/json/ld/compact.rb +75 -27
  10. data/lib/json/ld/conneg.rb +188 -0
  11. data/lib/json/ld/context.rb +677 -292
  12. data/lib/json/ld/expand.rb +240 -75
  13. data/lib/json/ld/flatten.rb +5 -3
  14. data/lib/json/ld/format.rb +19 -19
  15. data/lib/json/ld/frame.rb +135 -85
  16. data/lib/json/ld/from_rdf.rb +44 -17
  17. data/lib/json/ld/html/nokogiri.rb +151 -0
  18. data/lib/json/ld/html/rexml.rb +186 -0
  19. data/lib/json/ld/reader.rb +25 -5
  20. data/lib/json/ld/resource.rb +2 -2
  21. data/lib/json/ld/streaming_writer.rb +3 -1
  22. data/lib/json/ld/to_rdf.rb +47 -17
  23. data/lib/json/ld/utils.rb +4 -2
  24. data/lib/json/ld/writer.rb +75 -14
  25. data/spec/api_spec.rb +13 -34
  26. data/spec/compact_spec.rb +968 -9
  27. data/spec/conneg_spec.rb +373 -0
  28. data/spec/context_spec.rb +447 -53
  29. data/spec/expand_spec.rb +1872 -416
  30. data/spec/flatten_spec.rb +434 -47
  31. data/spec/frame_spec.rb +979 -344
  32. data/spec/from_rdf_spec.rb +305 -5
  33. data/spec/spec_helper.rb +177 -0
  34. data/spec/streaming_writer_spec.rb +4 -4
  35. data/spec/suite_compact_spec.rb +2 -2
  36. data/spec/suite_expand_spec.rb +14 -2
  37. data/spec/suite_flatten_spec.rb +10 -2
  38. data/spec/suite_frame_spec.rb +3 -2
  39. data/spec/suite_from_rdf_spec.rb +2 -2
  40. data/spec/suite_helper.rb +55 -20
  41. data/spec/suite_html_spec.rb +22 -0
  42. data/spec/suite_http_spec.rb +35 -0
  43. data/spec/suite_remote_doc_spec.rb +2 -2
  44. data/spec/suite_to_rdf_spec.rb +14 -3
  45. data/spec/support/extensions.rb +5 -1
  46. data/spec/test-files/test-4-input.json +3 -3
  47. data/spec/test-files/test-5-input.json +2 -2
  48. data/spec/test-files/test-8-framed.json +14 -18
  49. data/spec/to_rdf_spec.rb +606 -16
  50. data/spec/writer_spec.rb +5 -5
  51. metadata +144 -88
@@ -162,6 +162,21 @@ describe JSON::LD::API do
162
162
  "http://example.com/foo": [{"@id": "http://example.org/bar"}]
163
163
  }])
164
164
  },
165
+ "json-ld-syntax#66": {
166
+ input: %({
167
+ "@context": {
168
+ "@base": "https://ex.org/",
169
+ "u": {"@id": "urn:u:", "@type": "@id"}
170
+ },
171
+ "u": ["#Test", "#Test:2"]
172
+ }),
173
+ output: %([{
174
+ "urn:u:": [
175
+ {"@id": "https://ex.org/#Test"},
176
+ {"@id": "https://ex.org/#Test:2"}
177
+ ]
178
+ }])
179
+ }
165
180
  }.each do |title, params|
166
181
  it(title) {run_expand params.merge(base: "http://example.org/")}
167
182
  end
@@ -300,6 +315,65 @@ describe JSON::LD::API do
300
315
  it(title) {run_expand params.merge(base: "http://example.org/")}
301
316
  end
302
317
  end
318
+
319
+ context "with @vocab: '/relative#'" do
320
+ {
321
+ "base": {
322
+ input: %({
323
+ "@context": {"@vocab": "/relative#"},
324
+ "http://a/b": "foo"
325
+ }),
326
+ output: %([{
327
+ "http://a/b": [{"@value": "foo"}]
328
+ }])
329
+ },
330
+ "relative": {
331
+ input: %({
332
+ "@context": {"@vocab": "/relative#"},
333
+ "a/b": "foo"
334
+ }),
335
+ output: %([{
336
+ "http://example.org/relative#a/b": [{"@value": "foo"}]
337
+ }])
338
+ },
339
+ "hash": {
340
+ input: %({
341
+ "@context": {"@vocab": "/relative#"},
342
+ "#a": "foo"
343
+ }),
344
+ output: %([{
345
+ "http://example.org/relative##a": [{"@value": "foo"}]
346
+ }])
347
+ },
348
+ "dotseg": {
349
+ input: %({
350
+ "@context": {"@vocab": "/relative#"},
351
+ "../a": "foo"
352
+ }),
353
+ output: %([{
354
+ "http://example.org/relative#../a": [{"@value": "foo"}]
355
+ }])
356
+ },
357
+ "example": {
358
+ input: %({
359
+ "@context": {
360
+ "@base": "http://example/document",
361
+ "@vocab": "/relative#"
362
+ },
363
+ "@id": "http://example.org/places#BrewEats",
364
+ "@type": "Restaurant",
365
+ "name": "Brew Eats"
366
+ }),
367
+ output: %([{
368
+ "@id": "http://example.org/places#BrewEats",
369
+ "@type": ["http://example/relative#Restaurant"],
370
+ "http://example/relative#name": [{"@value": "Brew Eats"}]
371
+ }])
372
+ }
373
+ }.each do |title, params|
374
+ it(title) {run_expand params.merge(base: "http://example.org/")}
375
+ end
376
+ end
303
377
  end
304
378
 
305
379
  context "keyword aliasing" do
@@ -409,6 +483,58 @@ describe JSON::LD::API do
409
483
  it(title) {run_expand params}
410
484
  end
411
485
 
486
+ context "with @type: @none" do
487
+ {
488
+ "true": {
489
+ input: %({
490
+ "@context": {"e": {"@id": "http://example.org/vocab#bool", "@type": "@none"}},
491
+ "e": true
492
+ }),
493
+ output:%( [{
494
+ "http://example.org/vocab#bool": [{"@value": true}]
495
+ }])
496
+ },
497
+ "false": {
498
+ input: %({
499
+ "@context": {"e": {"@id": "http://example.org/vocab#bool", "@type": "@none"}},
500
+ "e": false
501
+ }),
502
+ output: %([{
503
+ "http://example.org/vocab#bool": [{"@value": false}]
504
+ }])
505
+ },
506
+ "double": {
507
+ input: %({
508
+ "@context": {"e": {"@id": "http://example.org/vocab#double", "@type": "@none"}},
509
+ "e": 1.23
510
+ }),
511
+ output: %([{
512
+ "http://example.org/vocab#double": [{"@value": 1.23}]
513
+ }])
514
+ },
515
+ "double-zero": {
516
+ input: %({
517
+ "@context": {"e": {"@id": "http://example.org/vocab#double", "@type": "@none"}},
518
+ "e": 0.0e0
519
+ }),
520
+ output: %([{
521
+ "http://example.org/vocab#double": [{"@value": 0.0e0}]
522
+ }])
523
+ },
524
+ "integer": {
525
+ input: %({
526
+ "@context": {"e": {"@id": "http://example.org/vocab#integer", "@type": "@none"}},
527
+ "e": 123
528
+ }),
529
+ output: %([{
530
+ "http://example.org/vocab#integer": [{"@value": 123}]
531
+ }])
532
+ },
533
+ }.each do |title, params|
534
+ it(title) {run_expand(processingMode: "json-ld-1.1", **params)}
535
+ end
536
+ end
537
+
412
538
  context "with @type: @id" do
413
539
  {
414
540
  "true": {
@@ -514,25 +640,176 @@ describe JSON::LD::API do
514
640
  end
515
641
  end
516
642
 
643
+ context "with @type: @json" do
644
+ {
645
+ "true": {
646
+ input: %({
647
+ "@context": {
648
+ "@version": 1.1,
649
+ "e": {"@id": "http://example.org/vocab#bool", "@type": "@json"}
650
+ },
651
+ "e": true
652
+ }),
653
+ output:%( [{
654
+ "http://example.org/vocab#bool": [{"@value": true, "@type": "@json"}]
655
+ }])
656
+ },
657
+ "false": {
658
+ input: %({
659
+ "@context": {
660
+ "@version": 1.1,
661
+ "e": {"@id": "http://example.org/vocab#bool", "@type": "@json"}
662
+ },
663
+ "e": false
664
+ }),
665
+ output: %([{
666
+ "http://example.org/vocab#bool": [{"@value": false, "@type": "@json"}]
667
+ }])
668
+ },
669
+ "double": {
670
+ input: %({
671
+ "@context": {
672
+ "@version": 1.1,
673
+ "e": {"@id": "http://example.org/vocab#double", "@type": "@json"}
674
+ },
675
+ "e": 1.23
676
+ }),
677
+ output: %([{
678
+ "http://example.org/vocab#double": [{"@value": 1.23, "@type": "@json"}]
679
+ }])
680
+ },
681
+ "double-zero": {
682
+ input: %({
683
+ "@context": {
684
+ "@version": 1.1,
685
+ "e": {"@id": "http://example.org/vocab#double", "@type": "@json"}
686
+ },
687
+ "e": 0.0e0
688
+ }),
689
+ output: %([{
690
+ "http://example.org/vocab#double": [{"@value": 0.0e0, "@type": "@json"}]
691
+ }])
692
+ },
693
+ "integer": {
694
+ input: %({
695
+ "@context": {
696
+ "@version": 1.1,
697
+ "e": {"@id": "http://example.org/vocab#integer", "@type": "@json"}
698
+ },
699
+ "e": 123
700
+ }),
701
+ output: %([{
702
+ "http://example.org/vocab#integer": [{"@value": 123, "@type": "@json"}]
703
+ }])
704
+ },
705
+ "string": {
706
+ input: %({
707
+ "@context": {
708
+ "@version": 1.1,
709
+ "e": {"@id": "http://example.org/vocab#string", "@type": "@json"}
710
+ },
711
+ "e": "string"
712
+ }),
713
+ output: %([{
714
+ "http://example.org/vocab#string": [{
715
+ "@value": "string",
716
+ "@type": "@json"
717
+ }]
718
+ }])
719
+ },
720
+ "null": {
721
+ input: %({
722
+ "@context": {
723
+ "@version": 1.1,
724
+ "e": {"@id": "http://example.org/vocab#null", "@type": "@json"}
725
+ },
726
+ "e": null
727
+ }),
728
+ output: %([{
729
+ "http://example.org/vocab#null": [{
730
+ "@value": null,
731
+ "@type": "@json"
732
+ }]
733
+ }])
734
+ },
735
+ "object": {
736
+ input: %({
737
+ "@context": {
738
+ "@version": 1.1,
739
+ "e": {"@id": "http://example.org/vocab#object", "@type": "@json"}
740
+ },
741
+ "e": {"foo": "bar"}
742
+ }),
743
+ output: %([{
744
+ "http://example.org/vocab#object": [{"@value": {"foo": "bar"}, "@type": "@json"}]
745
+ }])
746
+ },
747
+ "array": {
748
+ input: %({
749
+ "@context": {
750
+ "@version": 1.1,
751
+ "e": {"@id": "http://example.org/vocab#array", "@type": "@json"}
752
+ },
753
+ "e": [{"foo": "bar"}]
754
+ }),
755
+ output: %([{
756
+ "http://example.org/vocab#array": [{"@value": [{"foo": "bar"}], "@type": "@json"}]
757
+ }])
758
+ },
759
+ "Does not expand terms inside json": {
760
+ input: %({
761
+ "@context": {
762
+ "@version": 1.1,
763
+ "e": {"@id": "http://example.org/vocab#array", "@type": "@json"}
764
+ },
765
+ "e": [{"e": "bar"}]
766
+ }),
767
+ output: %([{
768
+ "http://example.org/vocab#array": [{"@value": [{"e": "bar"}], "@type": "@json"}]
769
+ }])
770
+ },
771
+ "Already expanded object": {
772
+ input: %({
773
+ "http://example.org/vocab#object": [{"@value": {"foo": "bar"}, "@type": "@json"}]
774
+ }),
775
+ output: %([{
776
+ "http://example.org/vocab#object": [{"@value": {"foo": "bar"}, "@type": "@json"}]
777
+ }]),
778
+ processingMode: 'json-ld-1.1'
779
+ },
780
+ "Already expanded object with aliased keys": {
781
+ input: %({
782
+ "@context": {"@version": 1.1, "value": "@value", "type": "@type", "json": "@json"},
783
+ "http://example.org/vocab#object": [{"value": {"foo": "bar"}, "type": "json"}]
784
+ }),
785
+ output: %([{
786
+ "http://example.org/vocab#object": [{"@value": {"foo": "bar"}, "@type": "@json"}]
787
+ }])
788
+ },
789
+ }.each do |title, params|
790
+ it(title) {run_expand params}
791
+ end
792
+ end
793
+
517
794
  context "coerced typed values" do
518
795
  {
519
- "boolean" => {
520
- input: {
521
- "@context" => {"foo" => {"@id" => "http://example.org/foo", "@type" => "http://www.w3.org/2001/XMLSchema#boolean"}},
522
- "foo" => "true"
523
- },
524
- output: [{
525
- "http://example.org/foo" => [{"@value" => "true", "@type" => "http://www.w3.org/2001/XMLSchema#boolean"}]
526
- }]
796
+ "boolean": {
797
+ input: %({
798
+ "@context": {"foo": {"@id": "http://example.org/foo", "@type": "http://www.w3.org/2001/XMLSchema#boolean"}},
799
+ "foo": "true"
800
+ }),
801
+ output: %([{
802
+ "http://example.org/foo": [{"@value": "true", "@type": "http://www.w3.org/2001/XMLSchema#boolean"}]
803
+ }])
527
804
  },
528
- "date" => {
529
- input: {
530
- "@context" => {"foo" => {"@id" => "http://example.org/foo", "@type" => "http://www.w3.org/2001/XMLSchema#date"}},
531
- "foo" => "2011-03-26"
532
- },
533
- output: [{
534
- "http://example.org/foo" => [{"@value" => "2011-03-26", "@type" => "http://www.w3.org/2001/XMLSchema#date"}]
535
- }]
805
+ "date": {
806
+ input: %({
807
+ "@context": {"foo": {"@id": "http://example.org/foo", "@type": "http://www.w3.org/2001/XMLSchema#date"}},
808
+ "foo": "2011-03-26"
809
+ }),
810
+ output: %([{
811
+ "http://example.org/foo": [{"@value": "2011-03-26", "@type": "http://www.w3.org/2001/XMLSchema#date"}]
812
+ }])
536
813
  },
537
814
  }.each do |title, params|
538
815
  it(title) {run_expand params}
@@ -541,127 +818,196 @@ describe JSON::LD::API do
541
818
 
542
819
  context "null" do
543
820
  {
544
- "value" => {
545
- input: {"http://example.com/foo" => nil},
821
+ "value": {
822
+ input: %({"http://example.com/foo": null}),
546
823
  output: []
547
824
  },
548
- "@value" => {
549
- input: {"http://example.com/foo" => {"@value" => nil}},
825
+ "@value": {
826
+ input: %({"http://example.com/foo": {"@value": null}}),
550
827
  output: []
551
828
  },
552
- "@value and non-null @type" => {
553
- input: {"http://example.com/foo" => {"@value" => nil, "@type" => "http://type"}},
829
+ "@value and non-null @type": {
830
+ input: %({"http://example.com/foo": {"@value": null, "@type": "http://type"}}),
554
831
  output: []
555
832
  },
556
- "@value and non-null @language" => {
557
- input: {"http://example.com/foo" => {"@value" => nil, "@language" => "en"}},
833
+ "@value and non-null @language": {
834
+ input: %({"http://example.com/foo": {"@value": null, "@language": "en"}}),
558
835
  output: []
559
836
  },
560
- "array with null elements" => {
561
- input: {
562
- "http://example.com/foo" => [nil]
563
- },
564
- output: [{
565
- "http://example.com/foo" => []
566
- }]
837
+ "array with null elements": {
838
+ input: %({"http://example.com/foo": [null]}),
839
+ output: %([{"http://example.com/foo": []}])
567
840
  },
568
- "@set with null @value" => {
569
- input: {
570
- "http://example.com/foo" => [
571
- {"@value" => nil, "@type" => "http://example.org/Type"}
841
+ "@set with null @value": {
842
+ input: %({
843
+ "http://example.com/foo": [
844
+ {"@value": null, "@type": "http://example.org/Type"}
572
845
  ]
573
- },
574
- output: [{
575
- "http://example.com/foo" => []
576
- }]
846
+ }),
847
+ output: %([{
848
+ "http://example.com/foo": []
849
+ }])
577
850
  }
578
851
  }.each do |title, params|
579
852
  it(title) {run_expand params}
580
853
  end
581
854
  end
582
855
 
856
+ context "@direction" do
857
+ {
858
+ "value with coerced null direction": {
859
+ input: %({
860
+ "@context": {
861
+ "@direction": "rtl",
862
+ "ex": "http://example.org/vocab#",
863
+ "ex:ltr": { "@direction": "ltr" },
864
+ "ex:none": { "@direction": null }
865
+ },
866
+ "ex:rtl": "rtl",
867
+ "ex:ltr": "ltr",
868
+ "ex:none": "no direction"
869
+ }),
870
+ output: %([
871
+ {
872
+ "http://example.org/vocab#rtl": [{"@value": "rtl", "@direction": "rtl"}],
873
+ "http://example.org/vocab#ltr": [{"@value": "ltr", "@direction": "ltr"}],
874
+ "http://example.org/vocab#none": [{"@value": "no direction"}]
875
+ }
876
+ ])
877
+ }
878
+ }.each_pair do |title, params|
879
+ it(title) {run_expand params}
880
+ end
881
+ end
882
+
583
883
  context "default language" do
584
884
  {
585
- "value with coerced null language" => {
586
- input: {
587
- "@context" => {
588
- "@language" => "en",
589
- "ex" => "http://example.org/vocab#",
590
- "ex:german" => { "@language" => "de" },
591
- "ex:nolang" => { "@language" => nil }
592
- },
593
- "ex:german" => "german",
594
- "ex:nolang" => "no language"
595
- },
596
- output: [
885
+ "value with coerced null language": {
886
+ input: %({
887
+ "@context": {
888
+ "@language": "en",
889
+ "ex": "http://example.org/vocab#",
890
+ "ex:german": { "@language": "de" },
891
+ "ex:nolang": { "@language": null }
892
+ },
893
+ "ex:german": "german",
894
+ "ex:nolang": "no language"
895
+ }),
896
+ output: %([
597
897
  {
598
- "http://example.org/vocab#german" => [{"@value" => "german", "@language" => "de"}],
599
- "http://example.org/vocab#nolang" => [{"@value" => "no language"}]
898
+ "http://example.org/vocab#german": [{"@value": "german", "@language": "de"}],
899
+ "http://example.org/vocab#nolang": [{"@value": "no language"}]
600
900
  }
601
- ]
901
+ ])
602
902
  },
603
903
  }.each do |title, params|
604
904
  it(title) {run_expand params}
605
905
  end
906
+
907
+ context "and default direction" do
908
+ {
909
+ "value with coerced null direction": {
910
+ input: %({
911
+ "@context": {
912
+ "@language": "en",
913
+ "@direction": "rtl",
914
+ "ex": "http://example.org/vocab#",
915
+ "ex:ltr": { "@direction": "ltr" },
916
+ "ex:none": { "@direction": null },
917
+ "ex:german": { "@language": "de" },
918
+ "ex:nolang": { "@language": null },
919
+ "ex:german_ltr": { "@language": "de", "@direction": "ltr" },
920
+ "ex:nolang_ltr": { "@language": null, "@direction": "ltr" },
921
+ "ex:none_none": { "@language": null, "@direction": null },
922
+ "ex:german_none": { "@language": "de", "@direction": null }
923
+ },
924
+ "ex:rtl": "rtl en",
925
+ "ex:ltr": "ltr en",
926
+ "ex:none": "no direction en",
927
+ "ex:german": "german rtl",
928
+ "ex:nolang": "no language rtl",
929
+ "ex:german_ltr": "german ltr",
930
+ "ex:nolang_ltr": "no language ltr",
931
+ "ex:none_none": "no language or direction",
932
+ "ex:german_none": "german no direction"
933
+ }),
934
+ output: %([
935
+ {
936
+ "http://example.org/vocab#rtl": [{"@value": "rtl en", "@language": "en", "@direction": "rtl"}],
937
+ "http://example.org/vocab#ltr": [{"@value": "ltr en", "@language": "en", "@direction": "ltr"}],
938
+ "http://example.org/vocab#none": [{"@value": "no direction en", "@language": "en"}],
939
+ "http://example.org/vocab#german": [{"@value": "german rtl", "@language": "de", "@direction": "rtl"}],
940
+ "http://example.org/vocab#nolang": [{"@value": "no language rtl", "@direction": "rtl"}],
941
+ "http://example.org/vocab#german_ltr": [{"@value": "german ltr", "@language": "de", "@direction": "ltr"}],
942
+ "http://example.org/vocab#nolang_ltr": [{"@value": "no language ltr", "@direction": "ltr"}],
943
+ "http://example.org/vocab#none_none": [{"@value": "no language or direction"}],
944
+ "http://example.org/vocab#german_none": [{"@value": "german no direction", "@language": "de"}]
945
+ }
946
+ ])
947
+ }
948
+ }.each_pair do |title, params|
949
+ it(title) {run_expand params}
950
+ end
951
+ end
606
952
  end
607
953
 
608
954
  context "default vocabulary" do
609
955
  {
610
- "property" => {
611
- input: {
612
- "@context" => {"@vocab" => "http://example.com/"},
613
- "verb" => {"@value" => "foo"}
614
- },
615
- output: [{
616
- "http://example.com/verb" => [{"@value" => "foo"}]
617
- }]
956
+ "property": {
957
+ input: %({
958
+ "@context": {"@vocab": "http://example.com/"},
959
+ "verb": {"@value": "foo"}
960
+ }),
961
+ output: %([{
962
+ "http://example.com/verb": [{"@value": "foo"}]
963
+ }])
618
964
  },
619
- "datatype" => {
620
- input: {
621
- "@context" => {"@vocab" => "http://example.com/"},
622
- "http://example.org/verb" => {"@value" => "foo", "@type" => "string"}
623
- },
624
- output: [
625
- "http://example.org/verb" => [{"@value" => "foo", "@type" => "http://example.com/string"}]
626
- ]
965
+ "datatype": {
966
+ input: %({
967
+ "@context": {"@vocab": "http://example.com/"},
968
+ "http://example.org/verb": {"@value": "foo", "@type": "string"}
969
+ }),
970
+ output: %([{
971
+ "http://example.org/verb": [{"@value": "foo", "@type": "http://example.com/string"}]
972
+ }])
627
973
  },
628
- "expand-0028" => {
629
- input: {
630
- "@context" => {
631
- "@vocab" => "http://example.org/vocab#",
632
- "date" => { "@type" => "dateTime" }
974
+ "expand-0028": {
975
+ input: %({
976
+ "@context": {
977
+ "@vocab": "http://example.org/vocab#",
978
+ "date": { "@type": "dateTime" }
633
979
  },
634
- "@id" => "example1",
635
- "@type" => "test",
636
- "date" => "2011-01-25T00:00:00Z",
637
- "embed" => {
638
- "@id" => "example2",
639
- "expandedDate" => { "@value" => "2012-08-01T00:00:00Z", "@type" => "dateTime" }
980
+ "@id": "example1",
981
+ "@type": "test",
982
+ "date": "2011-01-25T00:00:00Z",
983
+ "embed": {
984
+ "@id": "example2",
985
+ "expandedDate": { "@value": "2012-08-01T00:00:00Z", "@type": "dateTime" }
640
986
  }
641
- },
642
- output: [
987
+ }),
988
+ output: %([
643
989
  {
644
- "@id" => "http://foo/bar/example1",
645
- "@type" => ["http://example.org/vocab#test"],
646
- "http://example.org/vocab#date" => [
990
+ "@id": "http://foo/bar/example1",
991
+ "@type": ["http://example.org/vocab#test"],
992
+ "http://example.org/vocab#date": [
647
993
  {
648
- "@value" => "2011-01-25T00:00:00Z",
649
- "@type" => "http://example.org/vocab#dateTime"
994
+ "@value": "2011-01-25T00:00:00Z",
995
+ "@type": "http://example.org/vocab#dateTime"
650
996
  }
651
997
  ],
652
- "http://example.org/vocab#embed" => [
998
+ "http://example.org/vocab#embed": [
653
999
  {
654
- "@id" => "http://foo/bar/example2",
655
- "http://example.org/vocab#expandedDate" => [
1000
+ "@id": "http://foo/bar/example2",
1001
+ "http://example.org/vocab#expandedDate": [
656
1002
  {
657
- "@value" => "2012-08-01T00:00:00Z",
658
- "@type" => "http://example.org/vocab#dateTime"
1003
+ "@value": "2012-08-01T00:00:00Z",
1004
+ "@type": "http://example.org/vocab#dateTime"
659
1005
  }
660
1006
  ]
661
1007
  }
662
1008
  ]
663
1009
  }
664
- ]
1010
+ ])
665
1011
  }
666
1012
  }.each do |title, params|
667
1013
  it(title) {run_expand params.merge(base: "http://foo/bar/")}
@@ -670,51 +1016,47 @@ describe JSON::LD::API do
670
1016
 
671
1017
  context "unmapped properties" do
672
1018
  {
673
- "unmapped key" => {
674
- input: {
675
- "foo" => "bar"
676
- },
1019
+ "unmapped key": {
1020
+ input: %({"foo": "bar"}),
677
1021
  output: []
678
1022
  },
679
- "unmapped @type as datatype" => {
680
- input: {
681
- "http://example.com/foo" => {"@value" => "bar", "@type" => "baz"}
682
- },
683
- output: [{
684
- "http://example.com/foo" => [{"@value" => "bar", "@type" => "http://example/baz"}]
685
- }]
1023
+ "unmapped @type as datatype": {
1024
+ input: %({
1025
+ "http://example.com/foo": {"@value": "bar", "@type": "baz"}
1026
+ }),
1027
+ output: %([{
1028
+ "http://example.com/foo": [{"@value": "bar", "@type": "http://example/baz"}]
1029
+ }])
686
1030
  },
687
- "unknown keyword" => {
688
- input: {
689
- "@foo" => "bar"
690
- },
1031
+ "unknown keyword": {
1032
+ input: %({"@foo": "bar"}),
691
1033
  output: []
692
1034
  },
693
- "value" => {
694
- input: {
695
- "@context" => {"ex" => {"@id" => "http://example.org/idrange", "@type" => "@id"}},
696
- "@id" => "http://example.org/Subj",
697
- "idrange" => "unmapped"
698
- },
1035
+ "value": {
1036
+ input: %({
1037
+ "@context": {"ex": {"@id": "http://example.org/idrange", "@type": "@id"}},
1038
+ "@id": "http://example.org/Subj",
1039
+ "idrange": "unmapped"
1040
+ }),
699
1041
  output: []
700
1042
  },
701
- "context reset" => {
702
- input: {
703
- "@context" => {"ex" => "http://example.org/", "prop" => "ex:prop"},
704
- "@id" => "http://example.org/id1",
705
- "prop" => "prop",
706
- "ex:chain" => {
707
- "@context" => nil,
708
- "@id" => "http://example.org/id2",
709
- "prop" => "prop"
1043
+ "context reset": {
1044
+ input: %({
1045
+ "@context": {"ex": "http://example.org/", "prop": "ex:prop"},
1046
+ "@id": "http://example.org/id1",
1047
+ "prop": "prop",
1048
+ "ex:chain": {
1049
+ "@context": null,
1050
+ "@id": "http://example.org/id2",
1051
+ "prop": "prop"
710
1052
  }
711
- },
712
- output: [{
713
- "@id" => "http://example.org/id1",
714
- "http://example.org/prop" => [{"@value" => "prop"}],
715
- "http://example.org/chain" => [{"@id" => "http://example.org/id2"}]
716
- }
717
- ]}
1053
+ }),
1054
+ output: %([{
1055
+ "@id": "http://example.org/id1",
1056
+ "http://example.org/prop": [{"@value": "prop"}],
1057
+ "http://example.org/chain": [{"@id": "http://example.org/id2"}]
1058
+ }])
1059
+ }
718
1060
  }.each do |title, params|
719
1061
  it(title) {run_expand params.merge(base: "http://example/")}
720
1062
  end
@@ -722,92 +1064,311 @@ describe JSON::LD::API do
722
1064
 
723
1065
  context "@container: @index" do
724
1066
  {
725
- "string annotation" => {
726
- input: {
727
- "@context" => {
728
- "container" => {
729
- "@id" => "http://example.com/container",
730
- "@container" => "@index"
1067
+ "string annotation": {
1068
+ input: %({
1069
+ "@context": {
1070
+ "container": {
1071
+ "@id": "http://example.com/container",
1072
+ "@container": "@index"
731
1073
  }
732
1074
  },
733
- "@id" => "http://example.com/annotationsTest",
734
- "container" => {
735
- "en" => "The Queen",
736
- "de" => [ "Die Königin", "Ihre Majestät" ]
737
- }
738
- },
739
- output: [
740
- {
741
- "@id" => "http://example.com/annotationsTest",
742
- "http://example.com/container" => [
743
- {"@value" => "Die Königin", "@index" => "de"},
744
- {"@value" => "Ihre Majestät", "@index" => "de"},
745
- {"@value" => "The Queen", "@index" => "en"}
746
- ]
1075
+ "@id": "http://example.com/annotationsTest",
1076
+ "container": {
1077
+ "en": "The Queen",
1078
+ "de": [ "Die Königin", "Ihre Majestät" ]
747
1079
  }
748
- ]
1080
+ }),
1081
+ output: %([{
1082
+ "@id": "http://example.com/annotationsTest",
1083
+ "http://example.com/container": [
1084
+ {"@value": "Die Königin", "@index": "de"},
1085
+ {"@value": "Ihre Majestät", "@index": "de"},
1086
+ {"@value": "The Queen", "@index": "en"}
1087
+ ]
1088
+ }])
749
1089
  },
750
1090
  }.each do |title, params|
751
1091
  it(title) {run_expand params}
752
1092
  end
1093
+
1094
+ context "@index: property" do
1095
+ {
1096
+ "error if @version is json-ld-1.0": {
1097
+ input: %({
1098
+ "@context": {
1099
+ "@vocab": "http://example.com/",
1100
+ "container": {"@container": "@index", "@index": "prop"}
1101
+ },
1102
+ "@id": "http://example.com/annotationsTest",
1103
+ "container": {
1104
+ "en": "The Queen",
1105
+ "de": [ "Die Königin", "Ihre Majestät" ]
1106
+ }
1107
+ }),
1108
+ exception: JSON::LD::JsonLdError::InvalidTermDefinition,
1109
+ processingMode: 'json-ld-1.0'
1110
+ },
1111
+ "error if @container does not include @index": {
1112
+ input: %({
1113
+ "@context": {
1114
+ "@version": 1.1,
1115
+ "@vocab": "http://example.com/",
1116
+ "container": {"@index": "prop"}
1117
+ },
1118
+ "@id": "http://example.com/annotationsTest",
1119
+ "container": {
1120
+ "en": "The Queen",
1121
+ "de": [ "Die Königin", "Ihre Majestät" ]
1122
+ }
1123
+ }),
1124
+ exception: JSON::LD::JsonLdError::InvalidTermDefinition
1125
+ },
1126
+ "error if @index is a keyword": {
1127
+ input: %({
1128
+ "@context": {
1129
+ "@version": 1.1,
1130
+ "@vocab": "http://example.com/",
1131
+ "container": {
1132
+ "@id": "http://example.com/container",
1133
+ "@container": "@index",
1134
+ "@index": "@index"
1135
+ }
1136
+ },
1137
+ "@id": "http://example.com/annotationsTest",
1138
+ "container": {
1139
+ "en": "The Queen",
1140
+ "de": [ "Die Königin", "Ihre Majestät" ]
1141
+ }
1142
+ }),
1143
+ exception: JSON::LD::JsonLdError::InvalidTermDefinition
1144
+ },
1145
+ "error if @index is not a string": {
1146
+ input: %({
1147
+ "@context": {
1148
+ "@version": 1.1,
1149
+ "@vocab": "http://example.com/",
1150
+ "container": {
1151
+ "@id": "http://example.com/container",
1152
+ "@container": "@index",
1153
+ "@index": true
1154
+ }
1155
+ },
1156
+ "@id": "http://example.com/annotationsTest",
1157
+ "container": {
1158
+ "en": "The Queen",
1159
+ "de": [ "Die Königin", "Ihre Majestät" ]
1160
+ }
1161
+ }),
1162
+ exception: JSON::LD::JsonLdError::InvalidTermDefinition
1163
+ },
1164
+ "error if attempting to add property to value object": {
1165
+ input: %({
1166
+ "@context": {
1167
+ "@version": 1.1,
1168
+ "@vocab": "http://example.com/",
1169
+ "container": {
1170
+ "@id": "http://example.com/container",
1171
+ "@container": "@index",
1172
+ "@index": "prop"
1173
+ }
1174
+ },
1175
+ "@id": "http://example.com/annotationsTest",
1176
+ "container": {
1177
+ "en": "The Queen",
1178
+ "de": [ "Die Königin", "Ihre Majestät" ]
1179
+ }
1180
+ }),
1181
+ exception: JSON::LD::JsonLdError::InvalidValueObject
1182
+ },
1183
+ "property-valued index expands to property value, instead of @index (value)": {
1184
+ input: %({
1185
+ "@context": {
1186
+ "@version": 1.1,
1187
+ "@base": "http://example.com/",
1188
+ "@vocab": "http://example.com/",
1189
+ "author": {"@type": "@id", "@container": "@index", "@index": "prop"}
1190
+ },
1191
+ "@id": "article",
1192
+ "author": {
1193
+ "regular": "person/1",
1194
+ "guest": ["person/2", "person/3"]
1195
+ }
1196
+ }),
1197
+ output: %([{
1198
+ "@id": "http://example.com/article",
1199
+ "http://example.com/author": [
1200
+ {"@id": "http://example.com/person/1", "http://example.com/prop": [{"@value": "regular"}]},
1201
+ {"@id": "http://example.com/person/2", "http://example.com/prop": [{"@value": "guest"}]},
1202
+ {"@id": "http://example.com/person/3", "http://example.com/prop": [{"@value": "guest"}]}
1203
+ ]
1204
+ }])
1205
+ },
1206
+ "property-valued index appends to property value, instead of @index (value)": {
1207
+ input: %({
1208
+ "@context": {
1209
+ "@version": 1.1,
1210
+ "@base": "http://example.com/",
1211
+ "@vocab": "http://example.com/",
1212
+ "author": {"@type": "@id", "@container": "@index", "@index": "prop"}
1213
+ },
1214
+ "@id": "article",
1215
+ "author": {
1216
+ "regular": {"@id": "person/1", "http://example.com/prop": "foo"},
1217
+ "guest": [
1218
+ {"@id": "person/2", "prop": "foo"},
1219
+ {"@id": "person/3", "prop": "foo"}
1220
+ ]
1221
+ }
1222
+ }),
1223
+ output: %([{
1224
+ "@id": "http://example.com/article",
1225
+ "http://example.com/author": [
1226
+ {"@id": "http://example.com/person/1", "http://example.com/prop": [{"@value": "regular"}, {"@value": "foo"}]},
1227
+ {"@id": "http://example.com/person/2", "http://example.com/prop": [{"@value": "guest"}, {"@value": "foo"}]},
1228
+ {"@id": "http://example.com/person/3", "http://example.com/prop": [{"@value": "guest"}, {"@value": "foo"}]}
1229
+ ]
1230
+ }])
1231
+ },
1232
+ "property-valued index expands to property value, instead of @index (node)": {
1233
+ input: %({
1234
+ "@context": {
1235
+ "@version": 1.1,
1236
+ "@base": "http://example.com/",
1237
+ "@vocab": "http://example.com/",
1238
+ "author": {"@type": "@id", "@container": "@index", "@index": "prop"},
1239
+ "prop": {"@type": "@vocab"}
1240
+ },
1241
+ "@id": "http://example.com/article",
1242
+ "author": {
1243
+ "regular": "person/1",
1244
+ "guest": ["person/2", "person/3"]
1245
+ }
1246
+ }),
1247
+ output: %([{
1248
+ "@id": "http://example.com/article",
1249
+ "http://example.com/author": [
1250
+ {"@id": "http://example.com/person/1", "http://example.com/prop": [{"@id": "http://example.com/regular"}]},
1251
+ {"@id": "http://example.com/person/2", "http://example.com/prop": [{"@id": "http://example.com/guest"}]},
1252
+ {"@id": "http://example.com/person/3", "http://example.com/prop": [{"@id": "http://example.com/guest"}]}
1253
+ ]
1254
+ }])
1255
+ },
1256
+ "property-valued index appends to property value, instead of @index (node)": {
1257
+ input: %({
1258
+ "@context": {
1259
+ "@version": 1.1,
1260
+ "@base": "http://example.com/",
1261
+ "@vocab": "http://example.com/",
1262
+ "author": {"@type": "@id", "@container": "@index", "@index": "prop"},
1263
+ "prop": {"@type": "@vocab"}
1264
+ },
1265
+ "@id": "http://example.com/article",
1266
+ "author": {
1267
+ "regular": {"@id": "person/1", "prop": "foo"},
1268
+ "guest": [
1269
+ {"@id": "person/2", "prop": "foo"},
1270
+ {"@id": "person/3", "prop": "foo"}
1271
+ ]
1272
+ }
1273
+ }),
1274
+ output: %([{
1275
+ "@id": "http://example.com/article",
1276
+ "http://example.com/author": [
1277
+ {"@id": "http://example.com/person/1", "http://example.com/prop": [{"@id": "http://example.com/regular"}, {"@id": "http://example.com/foo"}]},
1278
+ {"@id": "http://example.com/person/2", "http://example.com/prop": [{"@id": "http://example.com/guest"}, {"@id": "http://example.com/foo"}]},
1279
+ {"@id": "http://example.com/person/3", "http://example.com/prop": [{"@id": "http://example.com/guest"}, {"@id": "http://example.com/foo"}]}
1280
+ ]
1281
+ }])
1282
+ },
1283
+ "property-valued index does not output property for @none": {
1284
+ input: %({
1285
+ "@context": {
1286
+ "@version": 1.1,
1287
+ "@base": "http://example.com/",
1288
+ "@vocab": "http://example.com/",
1289
+ "author": {"@type": "@id", "@container": "@index", "@index": "prop"},
1290
+ "prop": {"@type": "@vocab"}
1291
+ },
1292
+ "@id": "http://example.com/article",
1293
+ "author": {
1294
+ "@none": {"@id": "person/1"},
1295
+ "guest": [
1296
+ {"@id": "person/2"},
1297
+ {"@id": "person/3"}
1298
+ ]
1299
+ }
1300
+ }),
1301
+ output: %([{
1302
+ "@id": "http://example.com/article",
1303
+ "http://example.com/author": [
1304
+ {"@id": "http://example.com/person/1"},
1305
+ {"@id": "http://example.com/person/2", "http://example.com/prop": [{"@id": "http://example.com/guest"}]},
1306
+ {"@id": "http://example.com/person/3", "http://example.com/prop": [{"@id": "http://example.com/guest"}]}
1307
+ ]
1308
+ }])
1309
+ },
1310
+ }.each do |title, params|
1311
+ it(title) {run_expand(validate: true, **params)}
1312
+ end
1313
+ end
753
1314
  end
754
1315
 
755
1316
  context "@container: @list" do
756
1317
  {
757
- "empty" => {
758
- input: {"http://example.com/foo" => {"@list" => []}},
759
- output: [{"http://example.com/foo" => [{"@list" => []}]}]
760
- },
761
- "coerced empty" => {
762
- input: {
763
- "@context" => {"http://example.com/foo" => {"@container" => "@list"}},
764
- "http://example.com/foo" => []
765
- },
766
- output: [{"http://example.com/foo" => [{"@list" => []}]}]
1318
+ "empty": {
1319
+ input: %({"http://example.com/foo": {"@list": []}}),
1320
+ output: %([{"http://example.com/foo": [{"@list": []}]}])
767
1321
  },
768
- "coerced single element" => {
769
- input: {
770
- "@context" => {"http://example.com/foo" => {"@container" => "@list"}},
771
- "http://example.com/foo" => [ "foo" ]
772
- },
773
- output: [{"http://example.com/foo" => [{"@list" => [{"@value" => "foo"}]}]}]
1322
+ "coerced empty": {
1323
+ input: %({
1324
+ "@context": {"http://example.com/foo": {"@container": "@list"}},
1325
+ "http://example.com/foo": []
1326
+ }),
1327
+ output: %([{"http://example.com/foo": [{"@list": []}]}])
774
1328
  },
775
- "coerced multiple elements" => {
776
- input: {
777
- "@context" => {"http://example.com/foo" => {"@container" => "@list"}},
778
- "http://example.com/foo" => [ "foo", "bar" ]
779
- },
780
- output: [{
781
- "http://example.com/foo" => [{"@list" => [ {"@value" => "foo"}, {"@value" => "bar"} ]}]
782
- }]
1329
+ "coerced single element": {
1330
+ input: %({
1331
+ "@context": {"http://example.com/foo": {"@container": "@list"}},
1332
+ "http://example.com/foo": [ "foo" ]
1333
+ }),
1334
+ output: %([{"http://example.com/foo": [{"@list": [{"@value": "foo"}]}]}])
783
1335
  },
784
- "native values in list" => {
785
- input: {
786
- "http://example.com/foo" => {"@list" => [1, 2]}
787
- },
788
- output: [{
789
- "http://example.com/foo" => [{"@list" => [{"@value" => 1}, {"@value" => 2}]}]
790
- }]
1336
+ "coerced multiple elements": {
1337
+ input: %({
1338
+ "@context": {"http://example.com/foo": {"@container": "@list"}},
1339
+ "http://example.com/foo": [ "foo", "bar" ]
1340
+ }),
1341
+ output: %([{
1342
+ "http://example.com/foo": [{"@list": [ {"@value": "foo"}, {"@value": "bar"} ]}]
1343
+ }])
791
1344
  },
792
- "explicit list with coerced @id values" => {
793
- input: {
794
- "@context" => {"http://example.com/foo" => {"@type" => "@id"}},
795
- "http://example.com/foo" => {"@list" => ["http://foo", "http://bar"]}
796
- },
797
- output: [{
798
- "http://example.com/foo" => [{"@list" => [{"@id" => "http://foo"}, {"@id" => "http://bar"}]}]
799
- }]
1345
+ "native values in list": {
1346
+ input: %({
1347
+ "http://example.com/foo": {"@list": [1, 2]}
1348
+ }),
1349
+ output: %([{
1350
+ "http://example.com/foo": [{"@list": [{"@value": 1}, {"@value": 2}]}]
1351
+ }])
800
1352
  },
801
- "explicit list with coerced datatype values" => {
802
- input: {
803
- "@context" => {"http://example.com/foo" => {"@type" => RDF::XSD.date.to_s}},
804
- "http://example.com/foo" => {"@list" => ["2012-04-12"]}
805
- },
806
- output: [{
807
- "http://example.com/foo" => [{"@list" => [{"@value" => "2012-04-12", "@type" => RDF::XSD.date.to_s}]}]
808
- }]
1353
+ "explicit list with coerced @id values": {
1354
+ input: %({
1355
+ "@context": {"http://example.com/foo": {"@type": "@id"}},
1356
+ "http://example.com/foo": {"@list": ["http://foo", "http://bar"]}
1357
+ }),
1358
+ output: %([{
1359
+ "http://example.com/foo": [{"@list": [{"@id": "http://foo"}, {"@id": "http://bar"}]}]
1360
+ }])
1361
+ },
1362
+ "explicit list with coerced datatype values": {
1363
+ input: %({
1364
+ "@context": {"http://example.com/foo": {"@type": "http://www.w3.org/2001/XMLSchema#date"}},
1365
+ "http://example.com/foo": {"@list": ["2012-04-12"]}
1366
+ }),
1367
+ output: %([{
1368
+ "http://example.com/foo": [{"@list": [{"@value": "2012-04-12", "@type": "http://www.w3.org/2001/XMLSchema#date"}]}]
1369
+ }])
809
1370
  },
810
- "expand-0004" => {
1371
+ "expand-0004": {
811
1372
  input: %({
812
1373
  "@context": {
813
1374
  "mylist1": {"@id": "http://example.com/mylist1", "@container": "@list"},
@@ -831,7 +1392,7 @@ describe JSON::LD::API do
831
1392
  }
832
1393
  ])
833
1394
  },
834
- "@list containing @list" => {
1395
+ "@list containing @list": {
835
1396
  input: %({
836
1397
  "http://example.com/foo": {"@list": [{"@list": ["baz"]}]}
837
1398
  }),
@@ -839,7 +1400,7 @@ describe JSON::LD::API do
839
1400
  "http://example.com/foo": [{"@list": [{"@list": [{"@value": "baz"}]}]}]
840
1401
  }])
841
1402
  },
842
- "@list containing empty @list" => {
1403
+ "@list containing empty @list": {
843
1404
  input: %({
844
1405
  "http://example.com/foo": {"@list": [{"@list": []}]}
845
1406
  }),
@@ -847,7 +1408,7 @@ describe JSON::LD::API do
847
1408
  "http://example.com/foo": [{"@list": [{"@list": []}]}]
848
1409
  }])
849
1410
  },
850
- "@list containing @list (with coercion)" => {
1411
+ "@list containing @list (with coercion)": {
851
1412
  input: %({
852
1413
  "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
853
1414
  "foo": [{"@list": ["baz"]}]
@@ -856,7 +1417,7 @@ describe JSON::LD::API do
856
1417
  "http://example.com/foo": [{"@list": [{"@list": [{"@value": "baz"}]}]}]
857
1418
  }])
858
1419
  },
859
- "@list containing empty @list (with coercion)" => {
1420
+ "@list containing empty @list (with coercion)": {
860
1421
  input: %({
861
1422
  "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
862
1423
  "foo": [{"@list": []}]
@@ -865,7 +1426,7 @@ describe JSON::LD::API do
865
1426
  "http://example.com/foo": [{"@list": [{"@list": []}]}]
866
1427
  }])
867
1428
  },
868
- "coerced @list containing an array" => {
1429
+ "coerced @list containing an array": {
869
1430
  input: %({
870
1431
  "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
871
1432
  "foo": [["baz"]]
@@ -874,7 +1435,7 @@ describe JSON::LD::API do
874
1435
  "http://example.com/foo": [{"@list": [{"@list": [{"@value": "baz"}]}]}]
875
1436
  }])
876
1437
  },
877
- "coerced @list containing an empty array" => {
1438
+ "coerced @list containing an empty array": {
878
1439
  input: %({
879
1440
  "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
880
1441
  "foo": [[]]
@@ -883,7 +1444,7 @@ describe JSON::LD::API do
883
1444
  "http://example.com/foo": [{"@list": [{"@list": []}]}]
884
1445
  }])
885
1446
  },
886
- "coerced @list containing deep arrays" => {
1447
+ "coerced @list containing deep arrays": {
887
1448
  input: %({
888
1449
  "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
889
1450
  "foo": [[["baz"]]]
@@ -892,7 +1453,7 @@ describe JSON::LD::API do
892
1453
  "http://example.com/foo": [{"@list": [{"@list": [{"@list": [{"@value": "baz"}]}]}]}]
893
1454
  }])
894
1455
  },
895
- "coerced @list containing deep empty arrays" => {
1456
+ "coerced @list containing deep empty arrays": {
896
1457
  input: %({
897
1458
  "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
898
1459
  "foo": [[[]]]
@@ -901,7 +1462,7 @@ describe JSON::LD::API do
901
1462
  "http://example.com/foo": [{"@list": [{"@list": [{"@list": []}]}]}]
902
1463
  }]),
903
1464
  },
904
- "coerced @list containing multiple lists" => {
1465
+ "coerced @list containing multiple lists": {
905
1466
  input: %({
906
1467
  "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
907
1468
  "foo": [["a"], ["b"]]
@@ -913,7 +1474,7 @@ describe JSON::LD::API do
913
1474
  ]}]
914
1475
  }])
915
1476
  },
916
- "coerced @list containing mixed list values" => {
1477
+ "coerced @list containing mixed list values": {
917
1478
  input: %({
918
1479
  "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
919
1480
  "foo": [["a"], "b"]
@@ -932,50 +1493,46 @@ describe JSON::LD::API do
932
1493
 
933
1494
  context "@container: @set" do
934
1495
  {
935
- "empty" => {
936
- input: {
937
- "http://example.com/foo" => {"@set" => []}
938
- },
939
- output: [{
940
- "http://example.com/foo" => []
941
- }]
1496
+ "empty": {
1497
+ input: %({"http://example.com/foo": {"@set": []}}),
1498
+ output: %([{"http://example.com/foo": []}])
942
1499
  },
943
- "coerced empty" => {
944
- input: {
945
- "@context" => {"http://example.com/foo" => {"@container" => "@set"}},
946
- "http://example.com/foo" => []
947
- },
948
- output: [{
949
- "http://example.com/foo" => []
950
- }]
1500
+ "coerced empty": {
1501
+ input: %({
1502
+ "@context": {"http://example.com/foo": {"@container": "@set"}},
1503
+ "http://example.com/foo": []
1504
+ }),
1505
+ output: %([{
1506
+ "http://example.com/foo": []
1507
+ }])
951
1508
  },
952
- "coerced single element" => {
953
- input: {
954
- "@context" => {"http://example.com/foo" => {"@container" => "@set"}},
955
- "http://example.com/foo" => [ "foo" ]
956
- },
957
- output: [{
958
- "http://example.com/foo" => [ {"@value" => "foo"} ]
959
- }]
1509
+ "coerced single element": {
1510
+ input: %({
1511
+ "@context": {"http://example.com/foo": {"@container": "@set"}},
1512
+ "http://example.com/foo": [ "foo" ]
1513
+ }),
1514
+ output: %([{
1515
+ "http://example.com/foo": [ {"@value": "foo"} ]
1516
+ }])
960
1517
  },
961
- "coerced multiple elements" => {
962
- input: {
963
- "@context" => {"http://example.com/foo" => {"@container" => "@set"}},
964
- "http://example.com/foo" => [ "foo", "bar" ]
965
- },
966
- output: [{
967
- "http://example.com/foo" => [ {"@value" => "foo"}, {"@value" => "bar"} ]
968
- }]
1518
+ "coerced multiple elements": {
1519
+ input: %({
1520
+ "@context": {"http://example.com/foo": {"@container": "@set"}},
1521
+ "http://example.com/foo": [ "foo", "bar" ]
1522
+ }),
1523
+ output: %([{
1524
+ "http://example.com/foo": [ {"@value": "foo"}, {"@value": "bar"} ]
1525
+ }])
969
1526
  },
970
- "array containing set" => {
971
- input: {
972
- "http://example.com/foo" => [{"@set" => []}]
973
- },
974
- output: [{
975
- "http://example.com/foo" => []
976
- }]
1527
+ "array containing set": {
1528
+ input: %({
1529
+ "http://example.com/foo": [{"@set": []}]
1530
+ }),
1531
+ output: %([{
1532
+ "http://example.com/foo": []
1533
+ }])
977
1534
  },
978
- "Free-floating values in sets" => {
1535
+ "Free-floating values in sets": {
979
1536
  input: %({
980
1537
  "@context": {"property": "http://example.com/property"},
981
1538
  "@graph": [{
@@ -1005,120 +1562,230 @@ describe JSON::LD::API do
1005
1562
 
1006
1563
  context "@container: @language" do
1007
1564
  {
1008
- "simple map" => {
1009
- input: {
1010
- "@context" => {
1011
- "vocab" => "http://example.com/vocab/",
1012
- "label" => {
1013
- "@id" => "vocab:label",
1014
- "@container" => "@language"
1565
+ "simple map": {
1566
+ input: %({
1567
+ "@context": {
1568
+ "vocab": "http://example.com/vocab/",
1569
+ "label": {
1570
+ "@id": "vocab:label",
1571
+ "@container": "@language"
1015
1572
  }
1016
1573
  },
1017
- "@id" => "http://example.com/queen",
1018
- "label" => {
1019
- "en" => "The Queen",
1020
- "de" => [ "Die Königin", "Ihre Majestät" ]
1574
+ "@id": "http://example.com/queen",
1575
+ "label": {
1576
+ "en": "The Queen",
1577
+ "de": [ "Die Königin", "Ihre Majestät" ]
1021
1578
  }
1022
- },
1023
- output: [
1579
+ }),
1580
+ output: %([
1024
1581
  {
1025
- "@id" => "http://example.com/queen",
1026
- "http://example.com/vocab/label" => [
1027
- {"@value" => "Die Königin", "@language" => "de"},
1028
- {"@value" => "Ihre Majestät", "@language" => "de"},
1029
- {"@value" => "The Queen", "@language" => "en"}
1582
+ "@id": "http://example.com/queen",
1583
+ "http://example.com/vocab/label": [
1584
+ {"@value": "Die Königin", "@language": "de"},
1585
+ {"@value": "Ihre Majestät", "@language": "de"},
1586
+ {"@value": "The Queen", "@language": "en"}
1030
1587
  ]
1031
1588
  }
1032
- ]
1589
+ ])
1033
1590
  },
1034
- "simple map with @none" => {
1035
- input: {
1036
- "@context" => {
1037
- "vocab" => "http://example.com/vocab/",
1038
- "label" => {
1039
- "@id" => "vocab:label",
1040
- "@container" => "@language"
1591
+ "simple map with @none": {
1592
+ input: %({
1593
+ "@context": {
1594
+ "vocab": "http://example.com/vocab/",
1595
+ "label": {
1596
+ "@id": "vocab:label",
1597
+ "@container": "@language"
1041
1598
  }
1042
1599
  },
1043
- "@id" => "http://example.com/queen",
1044
- "label" => {
1045
- "en" => "The Queen",
1046
- "de" => [ "Die Königin", "Ihre Majestät" ],
1047
- "@none" => "The Queen"
1600
+ "@id": "http://example.com/queen",
1601
+ "label": {
1602
+ "en": "The Queen",
1603
+ "de": [ "Die Königin", "Ihre Majestät" ],
1604
+ "@none": "The Queen"
1048
1605
  }
1049
- },
1050
- output: [
1606
+ }),
1607
+ output: %([
1051
1608
  {
1052
- "@id" => "http://example.com/queen",
1053
- "http://example.com/vocab/label" => [
1054
- {"@value" => "The Queen"},
1055
- {"@value" => "Die Königin", "@language" => "de"},
1056
- {"@value" => "Ihre Majestät", "@language" => "de"},
1057
- {"@value" => "The Queen", "@language" => "en"},
1609
+ "@id": "http://example.com/queen",
1610
+ "http://example.com/vocab/label": [
1611
+ {"@value": "The Queen"},
1612
+ {"@value": "Die Königin", "@language": "de"},
1613
+ {"@value": "Ihre Majestät", "@language": "de"},
1614
+ {"@value": "The Queen", "@language": "en"}
1058
1615
  ]
1059
1616
  }
1060
- ]
1617
+ ])
1061
1618
  },
1062
- "simple map with alias of @none" => {
1063
- input: {
1064
- "@context" => {
1065
- "vocab" => "http://example.com/vocab/",
1066
- "label" => {
1067
- "@id" => "vocab:label",
1068
- "@container" => "@language"
1619
+ "simple map with alias of @none": {
1620
+ input: %({
1621
+ "@context": {
1622
+ "vocab": "http://example.com/vocab/",
1623
+ "label": {
1624
+ "@id": "vocab:label",
1625
+ "@container": "@language"
1069
1626
  },
1070
- "none" => "@none"
1627
+ "none": "@none"
1628
+ },
1629
+ "@id": "http://example.com/queen",
1630
+ "label": {
1631
+ "en": "The Queen",
1632
+ "de": [ "Die Königin", "Ihre Majestät" ],
1633
+ "none": "The Queen"
1634
+ }
1635
+ }),
1636
+ output: %([
1637
+ {
1638
+ "@id": "http://example.com/queen",
1639
+ "http://example.com/vocab/label": [
1640
+ {"@value": "Die Königin", "@language": "de"},
1641
+ {"@value": "Ihre Majestät", "@language": "de"},
1642
+ {"@value": "The Queen", "@language": "en"},
1643
+ {"@value": "The Queen"}
1644
+ ]
1645
+ }
1646
+ ])
1647
+ },
1648
+ "simple map with default direction": {
1649
+ input: %({
1650
+ "@context": {
1651
+ "@direction": "ltr",
1652
+ "vocab": "http://example.com/vocab/",
1653
+ "label": {
1654
+ "@id": "vocab:label",
1655
+ "@container": "@language"
1656
+ }
1657
+ },
1658
+ "@id": "http://example.com/queen",
1659
+ "label": {
1660
+ "en": "The Queen",
1661
+ "de": [ "Die Königin", "Ihre Majestät" ]
1662
+ }
1663
+ }),
1664
+ output: %([
1665
+ {
1666
+ "@id": "http://example.com/queen",
1667
+ "http://example.com/vocab/label": [
1668
+ {"@value": "Die Königin", "@language": "de", "@direction": "ltr"},
1669
+ {"@value": "Ihre Majestät", "@language": "de", "@direction": "ltr"},
1670
+ {"@value": "The Queen", "@language": "en", "@direction": "ltr"}
1671
+ ]
1672
+ }
1673
+ ])
1674
+ },
1675
+ "simple map with term direction": {
1676
+ input: %({
1677
+ "@context": {
1678
+ "vocab": "http://example.com/vocab/",
1679
+ "label": {
1680
+ "@id": "vocab:label",
1681
+ "@direction": "ltr",
1682
+ "@container": "@language"
1683
+ }
1684
+ },
1685
+ "@id": "http://example.com/queen",
1686
+ "label": {
1687
+ "en": "The Queen",
1688
+ "de": [ "Die Königin", "Ihre Majestät" ]
1689
+ }
1690
+ }),
1691
+ output: %([
1692
+ {
1693
+ "@id": "http://example.com/queen",
1694
+ "http://example.com/vocab/label": [
1695
+ {"@value": "Die Königin", "@language": "de", "@direction": "ltr"},
1696
+ {"@value": "Ihre Majestät", "@language": "de", "@direction": "ltr"},
1697
+ {"@value": "The Queen", "@language": "en", "@direction": "ltr"}
1698
+ ]
1699
+ }
1700
+ ])
1701
+ },
1702
+ "simple map with overriding term direction": {
1703
+ input: %({
1704
+ "@context": {
1705
+ "vocab": "http://example.com/vocab/",
1706
+ "@direction": "rtl",
1707
+ "label": {
1708
+ "@id": "vocab:label",
1709
+ "@direction": "ltr",
1710
+ "@container": "@language"
1711
+ }
1712
+ },
1713
+ "@id": "http://example.com/queen",
1714
+ "label": {
1715
+ "en": "The Queen",
1716
+ "de": [ "Die Königin", "Ihre Majestät" ]
1717
+ }
1718
+ }),
1719
+ output: %([
1720
+ {
1721
+ "@id": "http://example.com/queen",
1722
+ "http://example.com/vocab/label": [
1723
+ {"@value": "Die Königin", "@language": "de", "@direction": "ltr"},
1724
+ {"@value": "Ihre Majestät", "@language": "de", "@direction": "ltr"},
1725
+ {"@value": "The Queen", "@language": "en", "@direction": "ltr"}
1726
+ ]
1727
+ }
1728
+ ])
1729
+ },
1730
+ "simple map with overriding null direction": {
1731
+ input: %({
1732
+ "@context": {
1733
+ "vocab": "http://example.com/vocab/",
1734
+ "@direction": "rtl",
1735
+ "label": {
1736
+ "@id": "vocab:label",
1737
+ "@direction": null,
1738
+ "@container": "@language"
1739
+ }
1071
1740
  },
1072
- "@id" => "http://example.com/queen",
1073
- "label" => {
1074
- "en" => "The Queen",
1075
- "de" => [ "Die Königin", "Ihre Majestät" ],
1076
- "none" => "The Queen"
1741
+ "@id": "http://example.com/queen",
1742
+ "label": {
1743
+ "en": "The Queen",
1744
+ "de": [ "Die Königin", "Ihre Majestät" ]
1077
1745
  }
1078
- },
1079
- output: [
1746
+ }),
1747
+ output: %([
1080
1748
  {
1081
- "@id" => "http://example.com/queen",
1082
- "http://example.com/vocab/label" => [
1083
- {"@value" => "Die Königin", "@language" => "de"},
1084
- {"@value" => "Ihre Majestät", "@language" => "de"},
1085
- {"@value" => "The Queen", "@language" => "en"},
1086
- {"@value" => "The Queen"},
1749
+ "@id": "http://example.com/queen",
1750
+ "http://example.com/vocab/label": [
1751
+ {"@value": "Die Königin", "@language": "de"},
1752
+ {"@value": "Ihre Majestät", "@language": "de"},
1753
+ {"@value": "The Queen", "@language": "en"}
1087
1754
  ]
1088
1755
  }
1089
- ]
1756
+ ])
1090
1757
  },
1091
- "expand-0035" => {
1092
- input: {
1093
- "@context" => {
1094
- "@vocab" => "http://example.com/vocab/",
1095
- "@language" => "it",
1096
- "label" => {
1097
- "@container" => "@language"
1758
+ "expand-0035": {
1759
+ input: %({
1760
+ "@context": {
1761
+ "@vocab": "http://example.com/vocab/",
1762
+ "@language": "it",
1763
+ "label": {
1764
+ "@container": "@language"
1098
1765
  }
1099
1766
  },
1100
- "@id" => "http://example.com/queen",
1101
- "label" => {
1102
- "en" => "The Queen",
1103
- "de" => [ "Die Königin", "Ihre Majestät" ]
1767
+ "@id": "http://example.com/queen",
1768
+ "label": {
1769
+ "en": "The Queen",
1770
+ "de": [ "Die Königin", "Ihre Majestät" ]
1104
1771
  },
1105
- "http://example.com/vocab/label" => [
1772
+ "http://example.com/vocab/label": [
1106
1773
  "Il re",
1107
- { "@value" => "The king", "@language" => "en" }
1774
+ { "@value": "The king", "@language": "en" }
1108
1775
  ]
1109
- },
1110
- output: [
1776
+ }),
1777
+ output: %([
1111
1778
  {
1112
- "@id" => "http://example.com/queen",
1113
- "http://example.com/vocab/label" => [
1114
- {"@value" => "Il re", "@language" => "it"},
1115
- {"@value" => "The king", "@language" => "en"},
1116
- {"@value" => "Die Königin", "@language" => "de"},
1117
- {"@value" => "Ihre Majestät", "@language" => "de"},
1118
- {"@value" => "The Queen", "@language" => "en"},
1779
+ "@id": "http://example.com/queen",
1780
+ "http://example.com/vocab/label": [
1781
+ {"@value": "Il re", "@language": "it"},
1782
+ {"@value": "The king", "@language": "en"},
1783
+ {"@value": "Die Königin", "@language": "de"},
1784
+ {"@value": "Ihre Majestät", "@language": "de"},
1785
+ {"@value": "The Queen", "@language": "en"}
1119
1786
  ]
1120
1787
  }
1121
- ]
1788
+ ])
1122
1789
  }
1123
1790
  }.each do |title, params|
1124
1791
  it(title) {run_expand params}
@@ -1127,7 +1794,7 @@ describe JSON::LD::API do
1127
1794
 
1128
1795
  context "@container: @id" do
1129
1796
  {
1130
- "Adds @id to object not having an @id" => {
1797
+ "Adds @id to object not having an @id": {
1131
1798
  input: %({
1132
1799
  "@context": {
1133
1800
  "@vocab": "http://example/",
@@ -1145,7 +1812,7 @@ describe JSON::LD::API do
1145
1812
  ]
1146
1813
  }])
1147
1814
  },
1148
- "Retains @id in object already having an @id" => {
1815
+ "Retains @id in object already having an @id": {
1149
1816
  input: %({
1150
1817
  "@context": {
1151
1818
  "@vocab": "http://example/",
@@ -1163,7 +1830,7 @@ describe JSON::LD::API do
1163
1830
  ]
1164
1831
  }])
1165
1832
  },
1166
- "Adds expanded @id to object" => {
1833
+ "Adds expanded @id to object": {
1167
1834
  input: %({
1168
1835
  "@context": {
1169
1836
  "@vocab": "http://example/",
@@ -1180,7 +1847,7 @@ describe JSON::LD::API do
1180
1847
  }]),
1181
1848
  base: "http://example.org/"
1182
1849
  },
1183
- "Raises InvalidContainerMapping if processingMode is not specified" => {
1850
+ "Raises InvalidContainerMapping if processingMode is 1.0": {
1184
1851
  input: %({
1185
1852
  "@context": {
1186
1853
  "@vocab": "http://example/",
@@ -1191,7 +1858,7 @@ describe JSON::LD::API do
1191
1858
  "_:bar": {"label": "Object with @id _:bar"}
1192
1859
  }
1193
1860
  }),
1194
- processingMode: nil,
1861
+ processingMode: 'json-ld-1.0',
1195
1862
  exception: JSON::LD::JsonLdError::InvalidContainerMapping
1196
1863
  },
1197
1864
  "Does not add @id if it is @none, or expands to @none": {
@@ -1220,7 +1887,7 @@ describe JSON::LD::API do
1220
1887
 
1221
1888
  context "@container: @type" do
1222
1889
  {
1223
- "Adds @type to object not having an @type" => {
1890
+ "Adds @type to object not having an @type": {
1224
1891
  input: %({
1225
1892
  "@context": {
1226
1893
  "@vocab": "http://example/",
@@ -1238,7 +1905,7 @@ describe JSON::LD::API do
1238
1905
  ]
1239
1906
  }])
1240
1907
  },
1241
- "Prepends @type in object already having an @type" => {
1908
+ "Prepends @type in object already having an @type": {
1242
1909
  input: %({
1243
1910
  "@context": {
1244
1911
  "@vocab": "http://example/",
@@ -1262,7 +1929,7 @@ describe JSON::LD::API do
1262
1929
  ]
1263
1930
  }])
1264
1931
  },
1265
- "Adds vocabulary expanded @type to object" => {
1932
+ "Adds vocabulary expanded @type to object": {
1266
1933
  input: %({
1267
1934
  "@context": {
1268
1935
  "@vocab": "http://example/",
@@ -1278,7 +1945,7 @@ describe JSON::LD::API do
1278
1945
  ]
1279
1946
  }])
1280
1947
  },
1281
- "Adds document expanded @type to object" => {
1948
+ "Adds document expanded @type to object": {
1282
1949
  input: %({
1283
1950
  "@context": {
1284
1951
  "@vocab": "http://example/",
@@ -1314,7 +1981,7 @@ describe JSON::LD::API do
1314
1981
  ]
1315
1982
  }])
1316
1983
  },
1317
- "Raises InvalidContainerMapping if processingMode is not specified" => {
1984
+ "Raises InvalidContainerMapping if processingMode is 1.0": {
1318
1985
  input: %({
1319
1986
  "@context": {
1320
1987
  "@vocab": "http://example/",
@@ -1325,7 +1992,7 @@ describe JSON::LD::API do
1325
1992
  "_:bar": {"label": "Object with @type _:bar"}
1326
1993
  }
1327
1994
  }),
1328
- processingMode: nil,
1995
+ processingMode: 'json-ld-1.0',
1329
1996
  exception: JSON::LD::JsonLdError::InvalidContainerMapping
1330
1997
  },
1331
1998
  }.each do |title, params|
@@ -1335,7 +2002,7 @@ describe JSON::LD::API do
1335
2002
 
1336
2003
  context "@container: @graph" do
1337
2004
  {
1338
- "Creates a graph object given a value" => {
2005
+ "Creates a graph object given a value": {
1339
2006
  input: %({
1340
2007
  "@context": {
1341
2008
  "@vocab": "http://example.org/",
@@ -1353,7 +2020,7 @@ describe JSON::LD::API do
1353
2020
  }]
1354
2021
  }])
1355
2022
  },
1356
- "Creates a graph object within an array given a value" => {
2023
+ "Creates a graph object within an array given a value": {
1357
2024
  input: %({
1358
2025
  "@context": {
1359
2026
  "@vocab": "http://example.org/",
@@ -1371,7 +2038,7 @@ describe JSON::LD::API do
1371
2038
  }]
1372
2039
  }])
1373
2040
  },
1374
- "Does not create an graph object if value is a graph" => {
2041
+ "Creates an graph object if value is a graph": {
1375
2042
  input: %({
1376
2043
  "@context": {
1377
2044
  "@vocab": "http://example.org/",
@@ -1386,7 +2053,9 @@ describe JSON::LD::API do
1386
2053
  output: %([{
1387
2054
  "http://example.org/input": [{
1388
2055
  "@graph": [{
1389
- "http://example.org/value": [{"@value": "x"}]
2056
+ "@graph": [{
2057
+ "http://example.org/value": [{"@value": "x"}]
2058
+ }]
1390
2059
  }]
1391
2060
  }]
1392
2061
  }])
@@ -1397,7 +2066,7 @@ describe JSON::LD::API do
1397
2066
 
1398
2067
  context "+ @index" do
1399
2068
  {
1400
- "Creates a graph object given an indexed value" => {
2069
+ "Creates a graph object given an indexed value": {
1401
2070
  input: %({
1402
2071
  "@context": {
1403
2072
  "@vocab": "http://example.org/",
@@ -1416,7 +2085,7 @@ describe JSON::LD::API do
1416
2085
  }]
1417
2086
  }])
1418
2087
  },
1419
- "Creates a graph object given an indexed value with index @none" => {
2088
+ "Creates a graph object given an indexed value with index @none": {
1420
2089
  input: %({
1421
2090
  "@context": {
1422
2091
  "@vocab": "http://example.org/",
@@ -1434,7 +2103,7 @@ describe JSON::LD::API do
1434
2103
  }]
1435
2104
  }])
1436
2105
  },
1437
- "Creates a graph object given an indexed value with index alias of @none" => {
2106
+ "Creates a graph object given an indexed value with index alias of @none": {
1438
2107
  input: %({
1439
2108
  "@context": {
1440
2109
  "@vocab": "http://example.org/",
@@ -1453,7 +2122,7 @@ describe JSON::LD::API do
1453
2122
  }]
1454
2123
  }])
1455
2124
  },
1456
- "Creates a graph object given an indexed value with @set" => {
2125
+ "Creates a graph object given an indexed value with @set": {
1457
2126
  input: %({
1458
2127
  "@context": {
1459
2128
  "@vocab": "http://example.org/",
@@ -1472,7 +2141,7 @@ describe JSON::LD::API do
1472
2141
  }]
1473
2142
  }])
1474
2143
  },
1475
- "Does not create a new graph object if indexed value is already a graph object" => {
2144
+ "Does not create a new graph object if indexed value is already a graph object": {
1476
2145
  input: %({
1477
2146
  "@context": {
1478
2147
  "@vocab": "http://example.org/",
@@ -1498,11 +2167,38 @@ describe JSON::LD::API do
1498
2167
  }.each do |title, params|
1499
2168
  it(title) {run_expand({processingMode: "json-ld-1.1"}.merge(params))}
1500
2169
  end
2170
+
2171
+ context "@index: property" do
2172
+ {
2173
+ "it expands to property value, instead of @index": {
2174
+ input: %({
2175
+ "@context": {
2176
+ "@version": 1.1,
2177
+ "@vocab": "http://example.org/",
2178
+ "input": {"@container": ["@graph", "@index"], "@index": "prop"}
2179
+ },
2180
+ "input": {
2181
+ "g1": {"value": "x"}
2182
+ }
2183
+ }),
2184
+ output: %([{
2185
+ "http://example.org/input": [{
2186
+ "http://example.org/prop": [{"@value": "g1"}],
2187
+ "@graph": [{
2188
+ "http://example.org/value": [{"@value": "x"}]
2189
+ }]
2190
+ }]
2191
+ }])
2192
+ },
2193
+ }.each do |title, params|
2194
+ it(title) {run_expand(validate: true, **params)}
2195
+ end
2196
+ end
1501
2197
  end
1502
2198
 
1503
2199
  context "+ @id" do
1504
2200
  {
1505
- "Creates a graph object given an indexed value" => {
2201
+ "Creates a graph object given an indexed value": {
1506
2202
  input: %({
1507
2203
  "@context": {
1508
2204
  "@vocab": "http://example.org/",
@@ -1521,7 +2217,7 @@ describe JSON::LD::API do
1521
2217
  }]
1522
2218
  }])
1523
2219
  },
1524
- "Creates a graph object given an indexed value of @none" => {
2220
+ "Creates a graph object given an indexed value of @none": {
1525
2221
  input: %({
1526
2222
  "@context": {
1527
2223
  "@vocab": "http://example.org/",
@@ -1539,7 +2235,7 @@ describe JSON::LD::API do
1539
2235
  }]
1540
2236
  }])
1541
2237
  },
1542
- "Creates a graph object given an indexed value of alias of @none" => {
2238
+ "Creates a graph object given an indexed value of alias of @none": {
1543
2239
  input: %({
1544
2240
  "@context": {
1545
2241
  "@vocab": "http://example.org/",
@@ -1558,7 +2254,7 @@ describe JSON::LD::API do
1558
2254
  }]
1559
2255
  }])
1560
2256
  },
1561
- "Creates a graph object given an indexed value with @set" => {
2257
+ "Creates a graph object given an indexed value with @set": {
1562
2258
  input: %({
1563
2259
  "@context": {
1564
2260
  "@vocab": "http://example.org/",
@@ -1577,7 +2273,7 @@ describe JSON::LD::API do
1577
2273
  }]
1578
2274
  }])
1579
2275
  },
1580
- "Does not create a new graph object if indexed value is already a graph object" => {
2276
+ "Does not create a new graph object if indexed value is already a graph object": {
1581
2277
  input: %({
1582
2278
  "@context": {
1583
2279
  "@vocab": "http://example.org/",
@@ -1606,9 +2302,280 @@ describe JSON::LD::API do
1606
2302
  end
1607
2303
  end
1608
2304
 
2305
+ context "@included" do
2306
+ {
2307
+ "Basic Included array": {
2308
+ input: %({
2309
+ "@context": {
2310
+ "@version": 1.1,
2311
+ "@vocab": "http://example.org/"
2312
+ },
2313
+ "prop": "value",
2314
+ "@included": [{
2315
+ "prop": "value2"
2316
+ }]
2317
+ }),
2318
+ output: %([{
2319
+ "http://example.org/prop": [{"@value": "value"}],
2320
+ "@included": [{
2321
+ "http://example.org/prop": [{"@value": "value2"}]
2322
+ }]
2323
+ }])
2324
+ },
2325
+ "Basic Included object": {
2326
+ input: %({
2327
+ "@context": {
2328
+ "@version": 1.1,
2329
+ "@vocab": "http://example.org/"
2330
+ },
2331
+ "prop": "value",
2332
+ "@included": {
2333
+ "prop": "value2"
2334
+ }
2335
+ }),
2336
+ output: %([{
2337
+ "http://example.org/prop": [{"@value": "value"}],
2338
+ "@included": [{
2339
+ "http://example.org/prop": [{"@value": "value2"}]
2340
+ }]
2341
+ }])
2342
+ },
2343
+ "Multiple properties mapping to @included are folded together": {
2344
+ input: %({
2345
+ "@context": {
2346
+ "@version": 1.1,
2347
+ "@vocab": "http://example.org/",
2348
+ "included1": "@included",
2349
+ "included2": "@included"
2350
+ },
2351
+ "included1": {"prop": "value1"},
2352
+ "included2": {"prop": "value2"}
2353
+ }),
2354
+ output: %([{
2355
+ "@included": [
2356
+ {"http://example.org/prop": [{"@value": "value1"}]},
2357
+ {"http://example.org/prop": [{"@value": "value2"}]}
2358
+ ]
2359
+ }])
2360
+ },
2361
+ "Included containing @included": {
2362
+ input: %({
2363
+ "@context": {
2364
+ "@version": 1.1,
2365
+ "@vocab": "http://example.org/"
2366
+ },
2367
+ "prop": "value",
2368
+ "@included": {
2369
+ "prop": "value2",
2370
+ "@included": {
2371
+ "prop": "value3"
2372
+ }
2373
+ }
2374
+ }),
2375
+ output: %([{
2376
+ "http://example.org/prop": [{"@value": "value"}],
2377
+ "@included": [{
2378
+ "http://example.org/prop": [{"@value": "value2"}],
2379
+ "@included": [{
2380
+ "http://example.org/prop": [{"@value": "value3"}]
2381
+ }]
2382
+ }]
2383
+ }])
2384
+ },
2385
+ "Property value with @included": {
2386
+ input: %({
2387
+ "@context": {
2388
+ "@version": 1.1,
2389
+ "@vocab": "http://example.org/"
2390
+ },
2391
+ "prop": {
2392
+ "@type": "Foo",
2393
+ "@included": {
2394
+ "@type": "Bar"
2395
+ }
2396
+ }
2397
+ }),
2398
+ output: %([{
2399
+ "http://example.org/prop": [{
2400
+ "@type": ["http://example.org/Foo"],
2401
+ "@included": [{
2402
+ "@type": ["http://example.org/Bar"]
2403
+ }]
2404
+ }]
2405
+ }])
2406
+ },
2407
+ "json.api example": {
2408
+ input: %({
2409
+ "@context": {
2410
+ "@version": 1.1,
2411
+ "@vocab": "http://example.org/vocab#",
2412
+ "@base": "http://example.org/base/",
2413
+ "id": "@id",
2414
+ "type": "@type",
2415
+ "data": "@nest",
2416
+ "attributes": "@nest",
2417
+ "links": "@nest",
2418
+ "relationships": "@nest",
2419
+ "included": "@included",
2420
+ "self": {"@type": "@id"},
2421
+ "related": {"@type": "@id"},
2422
+ "comments": {
2423
+ "@context": {
2424
+ "data": null
2425
+ }
2426
+ }
2427
+ },
2428
+ "data": [{
2429
+ "type": "articles",
2430
+ "id": "1",
2431
+ "attributes": {
2432
+ "title": "JSON:API paints my bikeshed!"
2433
+ },
2434
+ "links": {
2435
+ "self": "http://example.com/articles/1"
2436
+ },
2437
+ "relationships": {
2438
+ "author": {
2439
+ "links": {
2440
+ "self": "http://example.com/articles/1/relationships/author",
2441
+ "related": "http://example.com/articles/1/author"
2442
+ },
2443
+ "data": { "type": "people", "id": "9" }
2444
+ },
2445
+ "comments": {
2446
+ "links": {
2447
+ "self": "http://example.com/articles/1/relationships/comments",
2448
+ "related": "http://example.com/articles/1/comments"
2449
+ },
2450
+ "data": [
2451
+ { "type": "comments", "id": "5" },
2452
+ { "type": "comments", "id": "12" }
2453
+ ]
2454
+ }
2455
+ }
2456
+ }],
2457
+ "included": [{
2458
+ "type": "people",
2459
+ "id": "9",
2460
+ "attributes": {
2461
+ "first-name": "Dan",
2462
+ "last-name": "Gebhardt",
2463
+ "twitter": "dgeb"
2464
+ },
2465
+ "links": {
2466
+ "self": "http://example.com/people/9"
2467
+ }
2468
+ }, {
2469
+ "type": "comments",
2470
+ "id": "5",
2471
+ "attributes": {
2472
+ "body": "First!"
2473
+ },
2474
+ "relationships": {
2475
+ "author": {
2476
+ "data": { "type": "people", "id": "2" }
2477
+ }
2478
+ },
2479
+ "links": {
2480
+ "self": "http://example.com/comments/5"
2481
+ }
2482
+ }, {
2483
+ "type": "comments",
2484
+ "id": "12",
2485
+ "attributes": {
2486
+ "body": "I like XML better"
2487
+ },
2488
+ "relationships": {
2489
+ "author": {
2490
+ "data": { "type": "people", "id": "9" }
2491
+ }
2492
+ },
2493
+ "links": {
2494
+ "self": "http://example.com/comments/12"
2495
+ }
2496
+ }]
2497
+ }),
2498
+ output: %([{
2499
+ "@id": "http://example.org/base/1",
2500
+ "@type": ["http://example.org/vocab#articles"],
2501
+ "http://example.org/vocab#title": [{"@value": "JSON:API paints my bikeshed!"}],
2502
+ "http://example.org/vocab#self": [{"@id": "http://example.com/articles/1"}],
2503
+ "http://example.org/vocab#author": [{
2504
+ "@id": "http://example.org/base/9",
2505
+ "@type": ["http://example.org/vocab#people"],
2506
+ "http://example.org/vocab#self": [{"@id": "http://example.com/articles/1/relationships/author"}],
2507
+ "http://example.org/vocab#related": [{"@id": "http://example.com/articles/1/author"}]
2508
+ }],
2509
+ "http://example.org/vocab#comments": [{
2510
+ "http://example.org/vocab#self": [{"@id": "http://example.com/articles/1/relationships/comments"}],
2511
+ "http://example.org/vocab#related": [{"@id": "http://example.com/articles/1/comments"}]
2512
+ }],
2513
+ "@included": [{
2514
+ "@id": "http://example.org/base/9",
2515
+ "@type": ["http://example.org/vocab#people"],
2516
+ "http://example.org/vocab#first-name": [{"@value": "Dan"}],
2517
+ "http://example.org/vocab#last-name": [{"@value": "Gebhardt"}],
2518
+ "http://example.org/vocab#twitter": [{"@value": "dgeb"}],
2519
+ "http://example.org/vocab#self": [{"@id": "http://example.com/people/9"}]
2520
+ }, {
2521
+ "@id": "http://example.org/base/5",
2522
+ "@type": ["http://example.org/vocab#comments"],
2523
+ "http://example.org/vocab#body": [{"@value": "First!"}],
2524
+ "http://example.org/vocab#author": [{
2525
+ "@id": "http://example.org/base/2",
2526
+ "@type": ["http://example.org/vocab#people"]
2527
+ }],
2528
+ "http://example.org/vocab#self": [{"@id": "http://example.com/comments/5"}]
2529
+ }, {
2530
+ "@id": "http://example.org/base/12",
2531
+ "@type": ["http://example.org/vocab#comments"],
2532
+ "http://example.org/vocab#body": [{"@value": "I like XML better"}],
2533
+ "http://example.org/vocab#author": [{
2534
+ "@id": "http://example.org/base/9",
2535
+ "@type": ["http://example.org/vocab#people"]
2536
+ }],
2537
+ "http://example.org/vocab#self": [{"@id": "http://example.com/comments/12"}]
2538
+ }]
2539
+ }])
2540
+ },
2541
+ "Error if @included value is a string": {
2542
+ input: %({
2543
+ "@context": {
2544
+ "@version": 1.1,
2545
+ "@vocab": "http://example.org/"
2546
+ },
2547
+ "@included": "string"
2548
+ }),
2549
+ exception: JSON::LD::JsonLdError::InvalidIncludedValue
2550
+ },
2551
+ "Error if @included value is a value object": {
2552
+ input: %({
2553
+ "@context": {
2554
+ "@version": 1.1,
2555
+ "@vocab": "http://example.org/"
2556
+ },
2557
+ "@included": {"@value": "value"}
2558
+ }),
2559
+ exception: JSON::LD::JsonLdError::InvalidIncludedValue
2560
+ },
2561
+ "Error if @included value is a list object": {
2562
+ input: %({
2563
+ "@context": {
2564
+ "@version": 1.1,
2565
+ "@vocab": "http://example.org/"
2566
+ },
2567
+ "@included": {"@list": ["value"]}
2568
+ }),
2569
+ exception: JSON::LD::JsonLdError::InvalidIncludedValue
2570
+ },
2571
+ }.each do |title, params|
2572
+ it(title) {run_expand(params)}
2573
+ end
2574
+ end
2575
+
1609
2576
  context "@nest" do
1610
2577
  {
1611
- "Expands input using @nest" => {
2578
+ "Expands input using @nest": {
1612
2579
  input: %({
1613
2580
  "@context": {"@vocab": "http://example.org/"},
1614
2581
  "p1": "v1",
@@ -1621,7 +2588,7 @@ describe JSON::LD::API do
1621
2588
  "http://example.org/p2": [{"@value": "v2"}]
1622
2589
  }])
1623
2590
  },
1624
- "Expands input using aliased @nest" => {
2591
+ "Expands input using aliased @nest": {
1625
2592
  input: %({
1626
2593
  "@context": {
1627
2594
  "@vocab": "http://example.org/",
@@ -1637,7 +2604,7 @@ describe JSON::LD::API do
1637
2604
  "http://example.org/p2": [{"@value": "v2"}]
1638
2605
  }])
1639
2606
  },
1640
- "Appends nested values when property at base and nested" => {
2607
+ "Appends nested values when property at base and nested": {
1641
2608
  input: %({
1642
2609
  "@context": {
1643
2610
  "@vocab": "http://example.org/",
@@ -1657,7 +2624,7 @@ describe JSON::LD::API do
1657
2624
  ]
1658
2625
  }])
1659
2626
  },
1660
- "Appends nested values from all @nest aliases in term order" => {
2627
+ "Appends nested values from all @nest aliases in term order": {
1661
2628
  input: %({
1662
2629
  "@context": {
1663
2630
  "@vocab": "http://example.org/",
@@ -1682,7 +2649,7 @@ describe JSON::LD::API do
1682
2649
  ]
1683
2650
  }])
1684
2651
  },
1685
- "Nested nested containers" => {
2652
+ "Nested nested containers": {
1686
2653
  input: %({
1687
2654
  "@context": {
1688
2655
  "@vocab": "http://example.org/"
@@ -1705,7 +2672,7 @@ describe JSON::LD::API do
1705
2672
  ]
1706
2673
  }])
1707
2674
  },
1708
- "Arrays of nested values" => {
2675
+ "Arrays of nested values": {
1709
2676
  input: %({
1710
2677
  "@context": {
1711
2678
  "@vocab": "http://example.org/",
@@ -1727,7 +2694,7 @@ describe JSON::LD::API do
1727
2694
  ]
1728
2695
  }])
1729
2696
  },
1730
- "A nest of arrays" => {
2697
+ "A nest of arrays": {
1731
2698
  input: %({
1732
2699
  "@context": {
1733
2700
  "@vocab": "http://example.org/",
@@ -1751,35 +2718,35 @@ describe JSON::LD::API do
1751
2718
  ]
1752
2719
  }])
1753
2720
  },
1754
- "@nest MUST NOT have a string value" => {
2721
+ "@nest MUST NOT have a string value": {
1755
2722
  input: %({
1756
2723
  "@context": {"@vocab": "http://example.org/"},
1757
2724
  "@nest": "This should generate an error"
1758
2725
  }),
1759
2726
  exception: JSON::LD::JsonLdError::InvalidNestValue
1760
2727
  },
1761
- "@nest MUST NOT have a boolen value" => {
2728
+ "@nest MUST NOT have a boolen value": {
1762
2729
  input: %({
1763
2730
  "@context": {"@vocab": "http://example.org/"},
1764
2731
  "@nest": true
1765
2732
  }),
1766
2733
  exception: JSON::LD::JsonLdError::InvalidNestValue
1767
2734
  },
1768
- "@nest MUST NOT have a numeric value" => {
2735
+ "@nest MUST NOT have a numeric value": {
1769
2736
  input: %({
1770
2737
  "@context": {"@vocab": "http://example.org/"},
1771
2738
  "@nest": 1
1772
2739
  }),
1773
2740
  exception: JSON::LD::JsonLdError::InvalidNestValue
1774
2741
  },
1775
- "@nest MUST NOT have a value object value" => {
2742
+ "@nest MUST NOT have a value object value": {
1776
2743
  input: %({
1777
2744
  "@context": {"@vocab": "http://example.org/"},
1778
2745
  "@nest": {"@value": "This should generate an error"}
1779
2746
  }),
1780
2747
  exception: JSON::LD::JsonLdError::InvalidNestValue
1781
2748
  },
1782
- "@nest in term definition MUST NOT be a non-@nest keyword" => {
2749
+ "@nest in term definition MUST NOT be a non-@nest keyword": {
1783
2750
  input: %({
1784
2751
  "@context": {
1785
2752
  "@vocab": "http://example.org/",
@@ -1789,7 +2756,7 @@ describe JSON::LD::API do
1789
2756
  }),
1790
2757
  exception: JSON::LD::JsonLdError::InvalidNestValue
1791
2758
  },
1792
- "@nest in term definition MUST NOT have a boolen value" => {
2759
+ "@nest in term definition MUST NOT have a boolen value": {
1793
2760
  input: %({
1794
2761
  "@context": {
1795
2762
  "@vocab": "http://example.org/",
@@ -1799,7 +2766,7 @@ describe JSON::LD::API do
1799
2766
  }),
1800
2767
  exception: JSON::LD::JsonLdError::InvalidNestValue
1801
2768
  },
1802
- "@nest in term definition MUST NOT have a numeric value" => {
2769
+ "@nest in term definition MUST NOT have a numeric value": {
1803
2770
  input: %({
1804
2771
  "@context": {
1805
2772
  "@vocab": "http://example.org/",
@@ -1809,7 +2776,7 @@ describe JSON::LD::API do
1809
2776
  }),
1810
2777
  exception: JSON::LD::JsonLdError::InvalidNestValue
1811
2778
  },
1812
- "Nested @container: @list" => {
2779
+ "Nested @container: @list": {
1813
2780
  input: %({
1814
2781
  "@context": {
1815
2782
  "@vocab": "http://example.org/",
@@ -1827,7 +2794,7 @@ describe JSON::LD::API do
1827
2794
  ]}]
1828
2795
  }])
1829
2796
  },
1830
- "Nested @container: @index" => {
2797
+ "Nested @container: @index": {
1831
2798
  input: %({
1832
2799
  "@context": {
1833
2800
  "@vocab": "http://example.org/",
@@ -1848,7 +2815,7 @@ describe JSON::LD::API do
1848
2815
  ]
1849
2816
  }])
1850
2817
  },
1851
- "Nested @container: @language" => {
2818
+ "Nested @container: @language": {
1852
2819
  input: %({
1853
2820
  "@context": {
1854
2821
  "@vocab": "http://example.org/",
@@ -1869,7 +2836,7 @@ describe JSON::LD::API do
1869
2836
  ]
1870
2837
  }])
1871
2838
  },
1872
- "Nested @container: @type" => {
2839
+ "Nested @container: @type": {
1873
2840
  input: %({
1874
2841
  "@context": {
1875
2842
  "@vocab": "http://example/",
@@ -1890,7 +2857,7 @@ describe JSON::LD::API do
1890
2857
  ]
1891
2858
  }])
1892
2859
  },
1893
- "Nested @container: @id" => {
2860
+ "Nested @container: @id": {
1894
2861
  input: %({
1895
2862
  "@context": {
1896
2863
  "@vocab": "http://example/",
@@ -1911,7 +2878,7 @@ describe JSON::LD::API do
1911
2878
  ]
1912
2879
  }])
1913
2880
  },
1914
- "Nest term an invalid keyword" => {
2881
+ "Nest term an invalid keyword": {
1915
2882
  input: %({
1916
2883
  "@context": {
1917
2884
  "term": {"@id": "http://example/term", "@nest": "@id"}
@@ -1919,7 +2886,7 @@ describe JSON::LD::API do
1919
2886
  }),
1920
2887
  exception: JSON::LD::JsonLdError::InvalidNestValue
1921
2888
  },
1922
- "Nest in @reverse" => {
2889
+ "Nest in @reverse": {
1923
2890
  input: %({
1924
2891
  "@context": {
1925
2892
  "term": {"@reverse": "http://example/term", "@nest": "@nest"}
@@ -1927,7 +2894,7 @@ describe JSON::LD::API do
1927
2894
  }),
1928
2895
  exception: JSON::LD::JsonLdError::InvalidReverseProperty
1929
2896
  },
1930
- "Raises InvalidTermDefinition if processingMode is not specified" => {
2897
+ "Raises InvalidTermDefinition if processingMode is 1.0": {
1931
2898
  input: %({
1932
2899
  "@context": {
1933
2900
  "@vocab": "http://example.org/",
@@ -1938,7 +2905,7 @@ describe JSON::LD::API do
1938
2905
  "list": ["a", "b"]
1939
2906
  }
1940
2907
  }),
1941
- processingMode: nil,
2908
+ processingMode: 'json-ld-1.0',
1942
2909
  validate: true,
1943
2910
  exception: JSON::LD::JsonLdError::InvalidTermDefinition
1944
2911
  },
@@ -1949,7 +2916,7 @@ describe JSON::LD::API do
1949
2916
 
1950
2917
  context "scoped context" do
1951
2918
  {
1952
- "adding new term" => {
2919
+ "adding new term": {
1953
2920
  input: %({
1954
2921
  "@context": {
1955
2922
  "@vocab": "http://example/",
@@ -1965,7 +2932,7 @@ describe JSON::LD::API do
1965
2932
  }
1966
2933
  ])
1967
2934
  },
1968
- "overriding a term" => {
2935
+ "overriding a term": {
1969
2936
  input: %({
1970
2937
  "@context": {
1971
2938
  "@vocab": "http://example/",
@@ -1982,7 +2949,7 @@ describe JSON::LD::API do
1982
2949
  }
1983
2950
  ])
1984
2951
  },
1985
- "property and value with different terms mapping to the same expanded property" => {
2952
+ "property and value with different terms mapping to the same expanded property": {
1986
2953
  input: %({
1987
2954
  "@context": {
1988
2955
  "@vocab": "http://example/",
@@ -2002,7 +2969,7 @@ describe JSON::LD::API do
2002
2969
  }
2003
2970
  ])
2004
2971
  },
2005
- "deep @context affects nested nodes" => {
2972
+ "deep @context affects nested nodes": {
2006
2973
  input: %({
2007
2974
  "@context": {
2008
2975
  "@vocab": "http://example/",
@@ -2024,7 +2991,7 @@ describe JSON::LD::API do
2024
2991
  }
2025
2992
  ])
2026
2993
  },
2027
- "scoped context layers on intemediate contexts" => {
2994
+ "scoped context layers on intemediate contexts": {
2028
2995
  input: %({
2029
2996
  "@context": {
2030
2997
  "@vocab": "http://example/",
@@ -2051,7 +3018,7 @@ describe JSON::LD::API do
2051
3018
  "http://example/c": [{"@value": "C in example"}]
2052
3019
  }])
2053
3020
  },
2054
- "Raises InvalidTermDefinition if processingMode is not specified" => {
3021
+ "Raises InvalidTermDefinition if processingMode is 1.0": {
2055
3022
  input: %({
2056
3023
  "@context": {
2057
3024
  "@vocab": "http://example/",
@@ -2061,10 +3028,59 @@ describe JSON::LD::API do
2061
3028
  "bar": "baz"
2062
3029
  }
2063
3030
  }),
2064
- processingMode: nil,
3031
+ processingMode: 'json-ld-1.0',
2065
3032
  validate: true,
2066
3033
  exception: JSON::LD::JsonLdError::InvalidTermDefinition
2067
3034
  },
3035
+ "Scoped on id map": {
3036
+ input: %({
3037
+ "@context": {
3038
+ "@version": 1.1,
3039
+ "schema": "http://schema.org/",
3040
+ "name": "schema:name",
3041
+ "body": "schema:articleBody",
3042
+ "words": "schema:wordCount",
3043
+ "post": {
3044
+ "@id": "schema:blogPost",
3045
+ "@container": "@id",
3046
+ "@context": {
3047
+ "@base": "http://example.com/posts/"
3048
+ }
3049
+ }
3050
+ },
3051
+ "@id": "http://example.com/",
3052
+ "@type": "schema:Blog",
3053
+ "name": "World Financial News",
3054
+ "post": {
3055
+ "1/en": {
3056
+ "body": "World commodities were up today with heavy trading of crude oil...",
3057
+ "words": 1539
3058
+ },
3059
+ "1/de": {
3060
+ "body": "Die Werte an Warenbörsen stiegen im Sog eines starken Handels von Rohöl...",
3061
+ "words": 1204
3062
+ }
3063
+ }
3064
+ }),
3065
+ output: %([{
3066
+ "@id": "http://example.com/",
3067
+ "@type": ["http://schema.org/Blog"],
3068
+ "http://schema.org/name": [{"@value": "World Financial News"}],
3069
+ "http://schema.org/blogPost": [{
3070
+ "@id": "http://example.com/posts/1/en",
3071
+ "http://schema.org/articleBody": [
3072
+ {"@value": "World commodities were up today with heavy trading of crude oil..."}
3073
+ ],
3074
+ "http://schema.org/wordCount": [{"@value": 1539}]
3075
+ }, {
3076
+ "@id": "http://example.com/posts/1/de",
3077
+ "http://schema.org/articleBody": [
3078
+ {"@value": "Die Werte an Warenbörsen stiegen im Sog eines starken Handels von Rohöl..."}
3079
+ ],
3080
+ "http://schema.org/wordCount": [{"@value": 1204}]
3081
+ }]
3082
+ }])
3083
+ }
2068
3084
  }.each do |title, params|
2069
3085
  it(title) {run_expand({processingMode: "json-ld-1.1"}.merge(params))}
2070
3086
  end
@@ -2072,7 +3088,7 @@ describe JSON::LD::API do
2072
3088
 
2073
3089
  context "scoped context on @type" do
2074
3090
  {
2075
- "adding new term" => {
3091
+ "adding new term": {
2076
3092
  input: %({
2077
3093
  "@context": {
2078
3094
  "@vocab": "http://example/",
@@ -2089,7 +3105,7 @@ describe JSON::LD::API do
2089
3105
  }
2090
3106
  ])
2091
3107
  },
2092
- "overriding a term" => {
3108
+ "overriding a term": {
2093
3109
  input: %({
2094
3110
  "@context": {
2095
3111
  "@vocab": "http://example/",
@@ -2107,7 +3123,7 @@ describe JSON::LD::API do
2107
3123
  }
2108
3124
  ])
2109
3125
  },
2110
- "alias of @type" => {
3126
+ "alias of @type": {
2111
3127
  input: %({
2112
3128
  "@context": {
2113
3129
  "@vocab": "http://example/",
@@ -2125,7 +3141,7 @@ describe JSON::LD::API do
2125
3141
  }
2126
3142
  ])
2127
3143
  },
2128
- "deep @context affects nested nodes" => {
3144
+ "deep @context does not affect nested nodes": {
2129
3145
  input: %({
2130
3146
  "@context": {
2131
3147
  "@vocab": "http://example/",
@@ -2138,12 +3154,12 @@ describe JSON::LD::API do
2138
3154
  {
2139
3155
  "@type": ["http://example/Foo"],
2140
3156
  "http://example/bar": [{
2141
- "http://example/baz": [{"@id": "http://example/buzz"}]
3157
+ "http://example/baz": [{"@value": "buzz"}]
2142
3158
  }]
2143
3159
  }
2144
3160
  ])
2145
3161
  },
2146
- "scoped context layers on intemediate contexts" => {
3162
+ "scoped context layers on intemediate contexts": {
2147
3163
  input: %({
2148
3164
  "@context": {
2149
3165
  "@vocab": "http://example/",
@@ -2166,7 +3182,7 @@ describe JSON::LD::API do
2166
3182
  "http://example/c": [{"@value": "C in example"}]
2167
3183
  }])
2168
3184
  },
2169
- "with @container: @type" => {
3185
+ "with @container: @type": {
2170
3186
  input: %({
2171
3187
  "@context": {
2172
3188
  "@vocab": "http://example/",
@@ -2183,7 +3199,7 @@ describe JSON::LD::API do
2183
3199
  ]
2184
3200
  }])
2185
3201
  },
2186
- "orders lexicographically" => {
3202
+ "orders lexicographically": {
2187
3203
  input: %({
2188
3204
  "@context": {
2189
3205
  "@vocab": "http://example/",
@@ -2200,7 +3216,7 @@ describe JSON::LD::API do
2200
3216
  ]
2201
3217
  }])
2202
3218
  },
2203
- "Raises InvalidTermDefinition if processingMode is not specified" => {
3219
+ "Raises InvalidTermDefinition if processingMode is 1.0": {
2204
3220
  input: %({
2205
3221
  "@context": {
2206
3222
  "@vocab": "http://example/",
@@ -2208,7 +3224,7 @@ describe JSON::LD::API do
2208
3224
  },
2209
3225
  "a": {"@type": "Foo", "bar": "baz"}
2210
3226
  }),
2211
- processingMode: nil,
3227
+ processingMode: 'json-ld-1.0',
2212
3228
  validate: true,
2213
3229
  exception: JSON::LD::JsonLdError::InvalidTermDefinition
2214
3230
  },
@@ -2219,7 +3235,7 @@ describe JSON::LD::API do
2219
3235
 
2220
3236
  context "@reverse" do
2221
3237
  {
2222
- "@container: @reverse" => {
3238
+ "@container: @reverse": {
2223
3239
  input: %({
2224
3240
  "@context": {
2225
3241
  "@vocab": "http://example/",
@@ -2239,7 +3255,7 @@ describe JSON::LD::API do
2239
3255
  }
2240
3256
  }])
2241
3257
  },
2242
- "expand-0037" => {
3258
+ "expand-0037": {
2243
3259
  input: %({
2244
3260
  "@context": {
2245
3261
  "name": "http://xmlns.com/foaf/0.1/name"
@@ -2276,7 +3292,7 @@ describe JSON::LD::API do
2276
3292
  }
2277
3293
  ])
2278
3294
  },
2279
- "expand-0043" => {
3295
+ "expand-0043": {
2280
3296
  input: %({
2281
3297
  "@context": {
2282
3298
  "name": "http://xmlns.com/foaf/0.1/name",
@@ -2326,7 +3342,7 @@ describe JSON::LD::API do
2326
3342
  }
2327
3343
  ])
2328
3344
  },
2329
- "@reverse object with an @id property" => {
3345
+ "@reverse object with an @id property": {
2330
3346
  input: %({
2331
3347
  "@id": "http://example/foo",
2332
3348
  "@reverse": {
@@ -2340,24 +3356,417 @@ describe JSON::LD::API do
2340
3356
  end
2341
3357
  end
2342
3358
 
3359
+ begin
3360
+ require 'nokogiri'
3361
+ rescue LoadError
3362
+ end
3363
+ require 'rexml/document'
3364
+
3365
+ context "html" do
3366
+ %w(Nokogiri REXML).each do |impl|
3367
+ next unless Module.constants.map(&:to_s).include?(impl)
3368
+ context impl do
3369
+ before(:all) {@library = impl.downcase.to_s.to_sym}
3370
+
3371
+ {
3372
+ "Expands embedded JSON-LD script element": {
3373
+ input: %(
3374
+ <html>
3375
+ <head>
3376
+ <script type="application/ld+json">
3377
+ {
3378
+ "@context": {
3379
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3380
+ },
3381
+ "foo": [{"@value": "bar"}]
3382
+ }
3383
+ </script>
3384
+ </head>
3385
+ </html>),
3386
+ output: %([{
3387
+ "http://example.com/foo": [{"@list": [{"@value": "bar"}]}]
3388
+ }])
3389
+ },
3390
+ "Expands first script element": {
3391
+ input: %(
3392
+ <html>
3393
+ <head>
3394
+ <script type="application/ld+json">
3395
+ {
3396
+ "@context": {
3397
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3398
+ },
3399
+ "foo": [{"@value": "bar"}]
3400
+ }
3401
+ </script>
3402
+ <script type="application/ld+json">
3403
+ {
3404
+ "@context": {"ex": "http://example.com/"},
3405
+ "@graph": [
3406
+ {"ex:foo": {"@value": "foo"}},
3407
+ {"ex:bar": {"@value": "bar"}}
3408
+ ]
3409
+ }
3410
+ </script>
3411
+ </head>
3412
+ </html>),
3413
+ output: %([{
3414
+ "http://example.com/foo": [{"@list": [{"@value": "bar"}]}]
3415
+ }])
3416
+ },
3417
+ "Expands targeted script element": {
3418
+ input: %(
3419
+ <html>
3420
+ <head>
3421
+ <script id="first" type="application/ld+json">
3422
+ {
3423
+ "@context": {
3424
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3425
+ },
3426
+ "foo": [{"@value": "bar"}]
3427
+ }
3428
+ </script>
3429
+ <script id="second" type="application/ld+json">
3430
+ {
3431
+ "@context": {"ex": "http://example.com/"},
3432
+ "@graph": [
3433
+ {"ex:foo": {"@value": "foo"}},
3434
+ {"ex:bar": {"@value": "bar"}}
3435
+ ]
3436
+ }
3437
+ </script>
3438
+ </head>
3439
+ </html>),
3440
+ output: %([
3441
+ {"http://example.com/foo": [{"@value": "foo"}]},
3442
+ {"http://example.com/bar": [{"@value": "bar"}]}
3443
+ ]),
3444
+ base: "http://example.org/doc#second"
3445
+ },
3446
+ "Expands all script elements with extractAllScripts option": {
3447
+ input: %(
3448
+ <html>
3449
+ <head>
3450
+ <script type="application/ld+json">
3451
+ {
3452
+ "@context": {
3453
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3454
+ },
3455
+ "foo": [{"@value": "bar"}]
3456
+ }
3457
+ </script>
3458
+ <script type="application/ld+json">
3459
+ {
3460
+ "@context": {"ex": "http://example.com/"},
3461
+ "@graph": [
3462
+ {"ex:foo": {"@value": "foo"}},
3463
+ {"ex:bar": {"@value": "bar"}}
3464
+ ]
3465
+ }
3466
+ </script>
3467
+ </head>
3468
+ </html>),
3469
+ output: %([
3470
+ {"http://example.com/foo": [{"@list": [{"@value": "bar"}]}]},
3471
+ {
3472
+ "@graph": [{
3473
+ "http://example.com/foo": [{"@value": "foo"}]
3474
+ }, {
3475
+ "http://example.com/bar": [{"@value": "bar"}]
3476
+ }]
3477
+ }
3478
+ ]),
3479
+ extractAllScripts: true
3480
+ },
3481
+ "Expands multiple scripts where one is an array": {
3482
+ input: %(
3483
+ <html>
3484
+ <head>
3485
+ <script type="application/ld+json">
3486
+ {
3487
+ "@context": {
3488
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3489
+ },
3490
+ "foo": [{"@value": "bar"}]
3491
+ }
3492
+ </script>
3493
+ <script type="application/ld+json">
3494
+ [
3495
+ {"@context": {"ex": "http://example.com/"}, "ex:foo": {"@value": "foo"}},
3496
+ {"@context": {"ex": "http://example.com/"}, "ex:bar": {"@value": "bar"}}
3497
+ ]
3498
+ </script>
3499
+ </head>
3500
+ </html>),
3501
+ output: %([
3502
+ {"http://example.com/foo": [{"@list": [{"@value": "bar"}]}]},
3503
+ {"http://example.com/foo": [{"@value": "foo"}]},
3504
+ {"http://example.com/bar": [{"@value": "bar"}]}
3505
+ ]),
3506
+ extractAllScripts: true
3507
+ },
3508
+ "Expands as empty with no script element": {
3509
+ input: %(<html><head></head></html>),
3510
+ output: %([])
3511
+ },
3512
+ "Expands as empty with no script element and extractAllScripts": {
3513
+ input: %(<html><head></head></html>),
3514
+ output: %([]),
3515
+ extractAllScripts: true
3516
+ },
3517
+ "Expands script element with HTML character references": {
3518
+ input: %(
3519
+ <html>
3520
+ <head>
3521
+ <script type="application/ld+json">
3522
+ {
3523
+ "@context": {"@vocab": "http://example/"},
3524
+ "foo": "&lt;&amp;&gt;"
3525
+ }
3526
+ </script>
3527
+ </head>
3528
+ </html>),
3529
+ output: %([{
3530
+ "http://example/foo": [{"@value": "&lt;&amp;&gt;"}]
3531
+ }])
3532
+ },
3533
+ "Expands embedded JSON-LD script element relative to document base": {
3534
+ input: %(
3535
+ <html>
3536
+ <head>
3537
+ <script type="application/ld+json">
3538
+ {
3539
+ "@context": {
3540
+ "foo": {"@id": "http://example.com/foo"}
3541
+ },
3542
+ "@id": "",
3543
+ "foo": [{"@value": "bar"}]
3544
+ }
3545
+ </script>
3546
+ </head>
3547
+ </html>),
3548
+ output: %([{
3549
+ "@id": "http://example.org/doc",
3550
+ "http://example.com/foo": [{"@value": "bar"}]
3551
+ }]),
3552
+ base: "http://example.org/doc"
3553
+ },
3554
+ "Expands embedded JSON-LD script element relative to HTML base": {
3555
+ input: %(
3556
+ <html>
3557
+ <head>
3558
+ <base href="http://example.org/base" />
3559
+ <script type="application/ld+json">
3560
+ {
3561
+ "@context": {
3562
+ "foo": {"@id": "http://example.com/foo"}
3563
+ },
3564
+ "@id": "",
3565
+ "foo": [{"@value": "bar"}]
3566
+ }
3567
+ </script>
3568
+ </head>
3569
+ </html>),
3570
+ output: %([{
3571
+ "@id": "http://example.org/base",
3572
+ "http://example.com/foo": [{"@value": "bar"}]
3573
+ }]),
3574
+ base: "http://example.org/doc"
3575
+ },
3576
+ "Expands embedded JSON-LD script element relative to relative HTML base": {
3577
+ input: %(
3578
+ <html>
3579
+ <head>
3580
+ <base href="base" />
3581
+ <script type="application/ld+json">
3582
+ {
3583
+ "@context": {
3584
+ "foo": {"@id": "http://example.com/foo"}
3585
+ },
3586
+ "@id": "",
3587
+ "foo": [{"@value": "bar"}]
3588
+ }
3589
+ </script>
3590
+ </head>
3591
+ </html>),
3592
+ output: %([{
3593
+ "@id": "http://example.org/base",
3594
+ "http://example.com/foo": [{"@value": "bar"}]
3595
+ }]),
3596
+ base: "http://example.org/doc"
3597
+ },
3598
+ "Errors if no element found at target": {
3599
+ input: %(
3600
+ <html>
3601
+ <head>
3602
+ <script id="first" type="application/ld+json">
3603
+ {
3604
+ "@context": {
3605
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3606
+ },
3607
+ "foo": [{"@value": "bar"}]
3608
+ }
3609
+ </script>
3610
+ <script id="second" type="application/ld+json">
3611
+ {
3612
+ "@context": {"ex": "http://example.com/"},
3613
+ "@graph": [
3614
+ {"ex:foo": {"@value": "foo"}},
3615
+ {"ex:bar": {"@value": "bar"}}
3616
+ ]
3617
+ }
3618
+ </script>
3619
+ </head>
3620
+ </html>),
3621
+ base: "http://example.org/doc#third",
3622
+ exception: JSON::LD::JsonLdError::InvalidScriptElement
3623
+ },
3624
+ "Errors if targeted element is not a script element": {
3625
+ input: %(
3626
+ <html>
3627
+ <head>
3628
+ <pre id="first" type="application/ld+json">
3629
+ {
3630
+ "@context": {
3631
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3632
+ },
3633
+ "foo": [{"@value": "bar"}]
3634
+ }
3635
+ </pre>
3636
+ </head>
3637
+ </html>),
3638
+ base: "http://example.org/doc#first",
3639
+ exception: JSON::LD::JsonLdError::InvalidScriptElement
3640
+ },
3641
+ "Errors if targeted element does not have type application/ld+json": {
3642
+ input: %(
3643
+ <html>
3644
+ <head>
3645
+ <script id="first" type="application/json">
3646
+ {
3647
+ "@context": {
3648
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3649
+ },
3650
+ "foo": [{"@value": "bar"}]
3651
+ }
3652
+ </script>
3653
+ </head>
3654
+ </html>),
3655
+ base: "http://example.org/doc#first",
3656
+ exception: JSON::LD::JsonLdError::InvalidScriptElement
3657
+ },
3658
+ "Errors if uncommented script text contains comment": {
3659
+ input: %(
3660
+ <html>
3661
+ <head>
3662
+ <script type="application/ld+json">
3663
+ <!--
3664
+ {
3665
+ "@context": {
3666
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3667
+ },
3668
+ "foo": [{"@value": "<!-- -->"}]
3669
+ }
3670
+ -->
3671
+ </script>
3672
+ </head>
3673
+ </html>),
3674
+ exception: JSON::LD::JsonLdError::InvalidScriptElement,
3675
+ not: :rexml
3676
+ },
3677
+ "Errors if end comment missing": {
3678
+ input: %(
3679
+ <html>
3680
+ <head>
3681
+ <script type="application/ld+json">
3682
+ <!--
3683
+ {
3684
+ "@context": {
3685
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3686
+ },
3687
+ "foo": [{"@value": "bar"}]
3688
+ }
3689
+ </script>
3690
+ </head>
3691
+ </html>),
3692
+ exception: JSON::LD::JsonLdError::InvalidScriptElement,
3693
+ not: :rexml
3694
+ },
3695
+ "Errors if start comment missing": {
3696
+ input: %(
3697
+ <html>
3698
+ <head>
3699
+ <script type="application/ld+json">
3700
+ {
3701
+ "@context": {
3702
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
3703
+ },
3704
+ "foo": [{"@value": "bar"}]
3705
+ }
3706
+ -->
3707
+ </script>
3708
+ </head>
3709
+ </html>),
3710
+ exception: JSON::LD::JsonLdError::InvalidScriptElement
3711
+ },
3712
+ "Errors if uncommented script is not valid JSON": {
3713
+ input: %(
3714
+ <html>
3715
+ <head>
3716
+ <script type="application/ld+json">
3717
+ foo
3718
+ </script>
3719
+ </head>
3720
+ </html>),
3721
+ exception: JSON::LD::JsonLdError::InvalidScriptElement
3722
+ },
3723
+ }.each do |title, params|
3724
+ it(title) do
3725
+ skip "rexml" if params[:not] == @library
3726
+ params[:input] = StringIO.new(params[:input])
3727
+ params[:input].send(:define_singleton_method, :content_type) {"text/html"}
3728
+ run_expand params.merge(validate: true, library: @library)
3729
+ end
3730
+ end
3731
+ end
3732
+ end
3733
+ end
3734
+
3735
+ context "deprectaions" do
3736
+ {
3737
+ "blank node property": {
3738
+ input: %({"_:bn": "value"}),
3739
+ output: %([{"_:bn": [{"@value": "value"}]}])
3740
+ }
3741
+ }.each do |name, params|
3742
+ it "deprecation on #{name} when validating" do
3743
+ run_expand(params.merge(validate: true, write: "[DEPRECATION]"))
3744
+ end
3745
+
3746
+ it "no deprecation on #{name} when not validating" do
3747
+ run_expand(params.merge(validate: false))
3748
+ end
3749
+ end
3750
+ end
3751
+
2343
3752
  context "exceptions" do
2344
3753
  {
2345
- "non-null @value and null @type" => {
2346
- input: {"http://example.com/foo" => {"@value" => "foo", "@type" => nil}},
3754
+ "non-null @value and null @type": {
3755
+ input: %({"http://example.com/foo": {"@value": "foo", "@type": null}}),
2347
3756
  exception: JSON::LD::JsonLdError::InvalidTypeValue
2348
3757
  },
2349
- "non-null @value and null @language" => {
2350
- input: {"http://example.com/foo" => {"@value" => "foo", "@language" => nil}},
3758
+ "non-null @value and null @language": {
3759
+ input: %({"http://example.com/foo": {"@value": "foo", "@language": null}}),
2351
3760
  exception: JSON::LD::JsonLdError::InvalidLanguageTaggedString
2352
3761
  },
2353
- "value with null language" => {
2354
- input: {
2355
- "@context" => {"@language" => "en"},
2356
- "http://example.org/nolang" => {"@value" => "no language", "@language" => nil}
2357
- },
3762
+ "value with null language": {
3763
+ input: %({
3764
+ "@context": {"@language": "en"},
3765
+ "http://example.org/nolang": {"@value": "no language", "@language": null}
3766
+ }),
2358
3767
  exception: JSON::LD::JsonLdError::InvalidLanguageTaggedString
2359
3768
  },
2360
- "colliding keywords" => {
3769
+ "colliding keywords": {
2361
3770
  input: %({
2362
3771
  "@context": {
2363
3772
  "id": "@id",
@@ -2367,7 +3776,45 @@ describe JSON::LD::API do
2367
3776
  "ID": "http://example/bar"
2368
3777
  }),
2369
3778
  exception: JSON::LD::JsonLdError::CollidingKeywords,
2370
- }
3779
+ },
3780
+ "@language and @type": {
3781
+ input: %({
3782
+ "ex:p": {
3783
+ "@value": "v",
3784
+ "@type": "ex:t",
3785
+ "@language": "en"
3786
+ }
3787
+ }),
3788
+ exception: JSON::LD::JsonLdError::InvalidValueObject,
3789
+ processingMode: 'json-ld-1.1'
3790
+ },
3791
+ "@direction and @type": {
3792
+ input: %({
3793
+ "ex:p": {
3794
+ "@value": "v",
3795
+ "@type": "ex:t",
3796
+ "@direction": "rtl"
3797
+ }
3798
+ }),
3799
+ exception: JSON::LD::JsonLdError::InvalidValueObject,
3800
+ processingMode: 'json-ld-1.1'
3801
+ },
3802
+ }.each do |title, params|
3803
+ it(title) {run_expand params}
3804
+ end
3805
+ end
3806
+
3807
+ context "problem cases" do
3808
+ {
3809
+ "toRdf/0118": {
3810
+ input: %({
3811
+ "@context": {"term": "_:term", "termId": { "@id": "term", "@type": "@id" }},
3812
+ "termId": "term:AppendedToBlankNode"
3813
+ }),
3814
+ output: %([{
3815
+ "_:term": [{"@id": "_:termAppendedToBlankNode"}]
3816
+ }])
3817
+ },
2371
3818
  }.each do |title, params|
2372
3819
  it(title) {run_expand params}
2373
3820
  end
@@ -2376,14 +3823,23 @@ describe JSON::LD::API do
2376
3823
 
2377
3824
  def run_expand(params)
2378
3825
  input, output = params[:input], params[:output]
3826
+ params[:base] ||= nil
2379
3827
  input = ::JSON.parse(input) if input.is_a?(String)
2380
3828
  output = ::JSON.parse(output) if output.is_a?(String)
2381
3829
  pending params.fetch(:pending, "test implementation") unless input
2382
3830
  if params[:exception]
2383
- expect {JSON::LD::API.expand(input, params)}.to raise_error(params[:exception])
3831
+ expect {JSON::LD::API.expand(input, **params)}.to raise_error(params[:exception])
2384
3832
  else
2385
- jld = JSON::LD::API.expand(input, {logger: logger}.merge(params))
3833
+ jld = nil
3834
+ if params[:write]
3835
+ expect{jld = JSON::LD::API.expand(input, logger: logger, **params)}.to write(params[:write]).to(:error)
3836
+ else
3837
+ expect{jld = JSON::LD::API.expand(input, logger: logger, **params)}.not_to write.to(:error)
3838
+ end
2386
3839
  expect(jld).to produce_jsonld(output, logger)
3840
+
3841
+ # Also expect result to produce itself
3842
+ expect(output).to produce_jsonld(output, logger)
2387
3843
  end
2388
3844
  end
2389
3845
  end