json-ld 3.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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