json_pure 1.0.0 → 1.4.6

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 (93) hide show
  1. data/CHANGES +155 -1
  2. data/COPYING +58 -0
  3. data/GPL +7 -7
  4. data/README +324 -45
  5. data/Rakefile +166 -124
  6. data/TODO +1 -1
  7. data/VERSION +1 -1
  8. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log +52 -0
  9. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat +1000 -0
  10. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat +1001 -0
  11. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat +900 -0
  12. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat +901 -0
  13. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat +1000 -0
  14. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat +1001 -0
  15. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log +261 -0
  16. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat +1000 -0
  17. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat +1001 -0
  18. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat +1000 -0
  19. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat +1001 -0
  20. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat +1000 -0
  21. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat +1001 -0
  22. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log +262 -0
  23. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat +1000 -0
  24. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat +1001 -0
  25. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log +82 -0
  26. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log +34 -0
  27. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat +900 -0
  28. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat +901 -0
  29. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log +81 -0
  30. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat +1000 -0
  31. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat +1001 -0
  32. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log +82 -0
  33. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat +1000 -0
  34. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat +1001 -0
  35. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log +82 -0
  36. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat +1000 -0
  37. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat +1001 -0
  38. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log +82 -0
  39. data/benchmarks/generator2_benchmark.rb +222 -0
  40. data/benchmarks/generator_benchmark.rb +224 -0
  41. data/benchmarks/ohai.json +1216 -0
  42. data/benchmarks/ohai.ruby +1 -0
  43. data/benchmarks/parser2_benchmark.rb +251 -0
  44. data/benchmarks/parser_benchmark.rb +259 -0
  45. data/bin/edit_json.rb +1 -3
  46. data/bin/prettify_json.rb +75 -0
  47. data/data/index.html +5 -4
  48. data/data/prototype.js +2764 -1095
  49. data/ext/json/ext/generator/extconf.rb +14 -3
  50. data/ext/json/ext/generator/generator.c +1022 -334
  51. data/ext/json/ext/generator/generator.h +197 -0
  52. data/ext/json/ext/parser/extconf.rb +9 -3
  53. data/ext/json/ext/parser/parser.c +961 -577
  54. data/ext/json/ext/parser/parser.h +71 -0
  55. data/ext/json/ext/parser/parser.rl +400 -123
  56. data/install.rb +0 -0
  57. data/lib/json/add/core.rb +148 -0
  58. data/lib/json/add/rails.rb +58 -0
  59. data/lib/json/common.rb +254 -47
  60. data/lib/json/editor.rb +236 -72
  61. data/lib/json/ext.rb +2 -0
  62. data/lib/json/pure/generator.rb +235 -117
  63. data/lib/json/pure/parser.rb +124 -25
  64. data/lib/json/pure.rb +5 -3
  65. data/lib/json/version.rb +1 -1
  66. data/lib/json.rb +2 -197
  67. data/tests/fixtures/fail18.json +1 -0
  68. data/tests/test_json.rb +181 -22
  69. data/tests/test_json_addition.rb +84 -16
  70. data/tests/test_json_encoding.rb +68 -0
  71. data/tests/test_json_fixtures.rb +9 -5
  72. data/tests/test_json_generate.rb +114 -14
  73. data/tests/test_json_rails.rb +144 -0
  74. data/tests/test_json_unicode.rb +35 -14
  75. data/tools/fuzz.rb +13 -7
  76. data/tools/server.rb +0 -1
  77. metadata +156 -122
  78. data/benchmarks/benchmark.txt +0 -133
  79. data/benchmarks/benchmark_generator.rb +0 -44
  80. data/benchmarks/benchmark_parser.rb +0 -22
  81. data/benchmarks/benchmark_rails.rb +0 -26
  82. data/ext/json/ext/generator/Makefile +0 -149
  83. data/ext/json/ext/generator/unicode.c +0 -184
  84. data/ext/json/ext/generator/unicode.h +0 -40
  85. data/ext/json/ext/parser/Makefile +0 -149
  86. data/ext/json/ext/parser/unicode.c +0 -156
  87. data/ext/json/ext/parser/unicode.h +0 -44
  88. data/tests/fixtures/pass18.json +0 -1
  89. data/tests/runner.rb +0 -24
  90. /data/tests/fixtures/{fail15.json → pass15.json} +0 -0
  91. /data/tests/fixtures/{fail16.json → pass16.json} +0 -0
  92. /data/tests/fixtures/{fail17.json → pass17.json} +0 -0
  93. /data/tests/fixtures/{fail26.json → pass26.json} +0 -0
data/lib/json/editor.rb CHANGED
@@ -5,6 +5,7 @@ require 'gtk2'
5
5
  require 'iconv'
6
6
  require 'json'
7
7
  require 'rbconfig'
8
+ require 'open-uri'
8
9
 
9
10
  module JSON
10
11
  module Editor
@@ -25,6 +26,15 @@ module JSON
25
26
  # The Nodes necessary for the tree representation of a JSON document
26
27
  ALL_NODES = (ALL_TYPES + %w[Key]).sort
27
28
 
29
+ DEFAULT_DIALOG_KEY_PRESS_HANDLER = lambda do |dialog, event|
30
+ case event.keyval
31
+ when Gdk::Keyval::GDK_Return
32
+ dialog.response Dialog::RESPONSE_ACCEPT
33
+ when Gdk::Keyval::GDK_Escape
34
+ dialog.response Dialog::RESPONSE_REJECT
35
+ end
36
+ end
37
+
28
38
  # Returns the Gdk::Pixbuf of the icon named _name_ from the icon cache.
29
39
  def Editor.fetch_icon(name)
30
40
  @icon_cache ||= {}
@@ -41,11 +51,13 @@ module JSON
41
51
  dialog = MessageDialog.new(window, Dialog::MODAL,
42
52
  MessageDialog::ERROR,
43
53
  MessageDialog::BUTTONS_CLOSE, text)
54
+ dialog.show_all
44
55
  dialog.run
45
56
  rescue TypeError
46
57
  dialog = MessageDialog.new(Editor.window, Dialog::MODAL,
47
58
  MessageDialog::ERROR,
48
59
  MessageDialog::BUTTONS_CLOSE, text)
60
+ dialog.show_all
49
61
  dialog.run
50
62
  ensure
51
63
  dialog.destroy if dialog
@@ -58,6 +70,7 @@ module JSON
58
70
  dialog = MessageDialog.new(window, Dialog::MODAL,
59
71
  MessageDialog::QUESTION,
60
72
  MessageDialog::BUTTONS_YES_NO, text)
73
+ dialog.show_all
61
74
  dialog.run do |response|
62
75
  return Gtk::Dialog::RESPONSE_YES === response
63
76
  end
@@ -68,6 +81,7 @@ module JSON
68
81
  # Convert the tree model starting from Gtk::TreeIter _iter_ into a Ruby
69
82
  # data structure and return it.
70
83
  def Editor.model2data(iter)
84
+ return nil if iter.nil?
71
85
  case iter.type
72
86
  when 'Hash'
73
87
  hash = {}
@@ -214,9 +228,18 @@ module JSON
214
228
  # Adds a Gtk::MenuItem to this instance's #menu. _label_ is the label
215
229
  # string, _klass_ is the item type, and _callback_ is the procedure, that
216
230
  # is called if the _item_ is activated.
217
- def add_item(label, klass = MenuItem, &callback)
231
+ def add_item(label, keyval = nil, klass = MenuItem, &callback)
232
+ label = "#{label} (C-#{keyval.chr})" if keyval
218
233
  item = klass.new(label)
219
234
  item.signal_connect(:activate, &callback)
235
+ if keyval
236
+ self.signal_connect(:'key-press-event') do |item, event|
237
+ if event.state & Gdk::Window::ModifierType::CONTROL_MASK != 0 and
238
+ event.keyval == keyval
239
+ callback.call item
240
+ end
241
+ end
242
+ end
220
243
  menu.append item
221
244
  item
222
245
  end
@@ -432,18 +455,18 @@ module JSON
432
455
 
433
456
  # Create the menu.
434
457
  def create
435
- add_item("Change node", &method(:change_node))
458
+ add_item("Change node", ?n, &method(:change_node))
436
459
  add_separator
437
- add_item("Cut node", &method(:cut_node))
438
- add_item("Copy node", &method(:copy_node))
439
- add_item("Paste node (appending)", &method(:paste_node_appending))
440
- add_item("Paste node (inserting before)",
460
+ add_item("Cut node", ?X, &method(:cut_node))
461
+ add_item("Copy node", ?C, &method(:copy_node))
462
+ add_item("Paste node (appending)", ?A, &method(:paste_node_appending))
463
+ add_item("Paste node (inserting before)", ?I,
441
464
  &method(:paste_node_inserting_before))
442
465
  add_separator
443
- add_item("Append new node", &method(:append_new_node))
444
- add_item("Insert new node before", &method(:insert_new_node))
466
+ add_item("Append new node", ?a, &method(:append_new_node))
467
+ add_item("Insert new node before", ?i, &method(:insert_new_node))
445
468
  add_separator
446
- add_item("Collapse/Expand node (recursively)",
469
+ add_item("Collapse/Expand node (recursively)", ?e,
447
470
  &method(:collapse_expand))
448
471
 
449
472
  menu.show_all
@@ -474,6 +497,10 @@ module JSON
474
497
  window.file_open
475
498
  end
476
499
 
500
+ def open_location(item)
501
+ window.location_open
502
+ end
503
+
477
504
  # Revert the current JSON document in the editor to the saved version.
478
505
  def revert(item)
479
506
  window.instance_eval do
@@ -501,13 +528,14 @@ module JSON
501
528
  title = MenuItem.new('File')
502
529
  title.submenu = menu
503
530
  add_item('New', &method(:new))
504
- add_item('Open', &method(:open))
531
+ add_item('Open', ?o, &method(:open))
532
+ add_item('Open location', ?l, &method(:open_location))
505
533
  add_item('Revert', &method(:revert))
506
534
  add_separator
507
- add_item('Save', &method(:save))
508
- add_item('Save As', &method(:save_as))
535
+ add_item('Save', ?s, &method(:save))
536
+ add_item('Save As', ?S, &method(:save_as))
509
537
  add_separator
510
- add_item('Quit', &method(:quit))
538
+ add_item('Quit', ?q, &method(:quit))
511
539
  title
512
540
  end
513
541
  end
@@ -516,17 +544,32 @@ module JSON
516
544
  class EditMenu
517
545
  include MenuExtension
518
546
 
547
+ # Copy data from model into primary clipboard.
548
+ def copy(item)
549
+ data = Editor.model2data(model.iter_first)
550
+ json = JSON.pretty_generate(data, :max_nesting => false)
551
+ c = Gtk::Clipboard.get(Gdk::Selection::PRIMARY)
552
+ c.text = json
553
+ end
554
+
555
+ # Copy json text from primary clipboard into model.
556
+ def paste(item)
557
+ c = Gtk::Clipboard.get(Gdk::Selection::PRIMARY)
558
+ if json = c.wait_for_text
559
+ window.ask_save if @changed
560
+ begin
561
+ window.edit json
562
+ rescue JSON::ParserError
563
+ window.clear
564
+ end
565
+ end
566
+ end
567
+
519
568
  # Find a string in all nodes' contents and select the found node in the
520
569
  # treeview.
521
570
  def find(item)
522
- search = ask_for_find_term or return
523
- begin
524
- @search = Regexp.new(search)
525
- rescue => e
526
- Editor.error_dialog(self, "Evaluation of regex /#{search}/ failed: #{e}!")
527
- return
528
- end
529
- iter = model.get_iter('0')
571
+ @search = ask_for_find_term(@search) or return
572
+ iter = model.get_iter('0') or return
530
573
  iter.recursive_each do |i|
531
574
  if @iter
532
575
  if @iter != i
@@ -602,10 +645,13 @@ module JSON
602
645
  def create
603
646
  title = MenuItem.new('Edit')
604
647
  title.submenu = menu
605
- add_item('Find', &method(:find))
606
- add_item('Find Again', &method(:find_again))
648
+ add_item('Copy', ?c, &method(:copy))
649
+ add_item('Paste', ?v, &method(:paste))
607
650
  add_separator
608
- add_item('Sort', &method(:sort))
651
+ add_item('Find', ?f, &method(:find))
652
+ add_item('Find Again', ?g, &method(:find_again))
653
+ add_separator
654
+ add_item('Sort', ?S, &method(:sort))
609
655
  title
610
656
  end
611
657
  end
@@ -636,8 +682,8 @@ module JSON
636
682
  def create
637
683
  title = MenuItem.new('Options')
638
684
  title.submenu = menu
639
- add_item('Collapsed nodes', CheckMenuItem, &method(:collapsed_nodes))
640
- @pretty_item = add_item('Pretty saving', CheckMenuItem,
685
+ add_item('Collapsed nodes', nil, CheckMenuItem, &method(:collapsed_nodes))
686
+ @pretty_item = add_item('Pretty saving', nil, CheckMenuItem,
641
687
  &method(:pretty_saving))
642
688
  @pretty_item.active = true
643
689
  window.unchange
@@ -723,7 +769,12 @@ module JSON
723
769
  iter.type, iter.content = 'FalseClass', 'false'
724
770
  end
725
771
  when 'Numeric'
726
- iter.content = (Integer(value) rescue Float(value) rescue 0).to_s
772
+ iter.content =
773
+ if value == 'Infinity'
774
+ value
775
+ else
776
+ (Integer(value) rescue Float(value) rescue 0).to_s
777
+ end
727
778
  when 'String'
728
779
  iter.content = value
729
780
  when 'Hash', 'Array'
@@ -784,12 +835,13 @@ module JSON
784
835
  [ Stock::OK, Dialog::RESPONSE_ACCEPT ],
785
836
  [ Stock::CANCEL, Dialog::RESPONSE_REJECT ]
786
837
  )
838
+ dialog.width_request = 640
787
839
 
788
840
  hbox = HBox.new(false, 5)
789
- hbox.pack_start(Label.new("Key:"))
841
+ hbox.pack_start(Label.new("Key:"), false)
790
842
  hbox.pack_start(key_input = Entry.new)
791
843
  key_input.text = @key || ''
792
- dialog.vbox.add(hbox)
844
+ dialog.vbox.pack_start(hbox, false)
793
845
  key_input.signal_connect(:activate) do
794
846
  if parent.any? { |c| c.content == key_input.text }
795
847
  toplevel.display_status('Key already exists in Hash!')
@@ -800,11 +852,11 @@ module JSON
800
852
  end
801
853
 
802
854
  hbox = HBox.new(false, 5)
803
- hbox.add(Label.new("Type:"))
855
+ hbox.pack_start(Label.new("Type:"), false)
804
856
  hbox.pack_start(type_input = ComboBox.new(true))
805
857
  ALL_TYPES.each { |t| type_input.append_text(t) }
806
858
  type_input.active = @type || 0
807
- dialog.vbox.add(hbox)
859
+ dialog.vbox.pack_start(hbox, false)
808
860
 
809
861
  type_input.signal_connect(:changed) do
810
862
  value_input.editable = false
@@ -824,12 +876,15 @@ module JSON
824
876
  end
825
877
 
826
878
  hbox = HBox.new(false, 5)
827
- hbox.add(Label.new("Value:"))
879
+ hbox.pack_start(Label.new("Value:"), false)
828
880
  hbox.pack_start(value_input = Entry.new)
881
+ value_input.width_chars = 60
829
882
  value_input.text = @value || ''
830
- dialog.vbox.add(hbox)
883
+ dialog.vbox.pack_start(hbox, false)
831
884
 
885
+ dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
832
886
  dialog.show_all
887
+ self.focus = dialog
833
888
  dialog.run do |response|
834
889
  if response == Dialog::RESPONSE_ACCEPT
835
890
  @key = key_input.text
@@ -854,7 +909,7 @@ module JSON
854
909
  [ Stock::CANCEL, Dialog::RESPONSE_REJECT ]
855
910
  )
856
911
  hbox = HBox.new(false, 5)
857
- hbox.add(Label.new("Type:"))
912
+ hbox.pack_start(Label.new("Type:"), false)
858
913
  hbox.pack_start(type_input = ComboBox.new(true))
859
914
  default_active = 0
860
915
  types = parent ? ALL_TYPES : CONTAINER_TYPES
@@ -865,26 +920,33 @@ module JSON
865
920
  end
866
921
  end
867
922
  type_input.active = default_active
868
- dialog.vbox.add(hbox)
923
+ dialog.vbox.pack_start(hbox, false)
869
924
  type_input.signal_connect(:changed) do
870
925
  configure_value(value_input, types[type_input.active])
871
926
  end
872
927
 
873
928
  hbox = HBox.new(false, 5)
874
- hbox.add(Label.new("Value:"))
929
+ hbox.pack_start(Label.new("Value:"), false)
875
930
  hbox.pack_start(value_input = Entry.new)
931
+ value_input.width_chars = 60
876
932
  value_input.text = value_text if value_text
877
933
  configure_value(value_input, types[type_input.active])
878
934
 
879
- dialog.vbox.add(hbox)
935
+ dialog.vbox.pack_start(hbox, false)
880
936
 
937
+ dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
881
938
  dialog.show_all
939
+ self.focus = dialog
882
940
  dialog.run do |response|
883
941
  if response == Dialog::RESPONSE_ACCEPT
884
942
  type = types[type_input.active]
885
943
  @content = case type
886
944
  when 'Numeric'
887
- Integer(value_input.text) rescue Float(value_input.text) rescue 0
945
+ if (t = value_input.text) == 'Infinity'
946
+ 1 / 0.0
947
+ else
948
+ Integer(t) rescue Float(t) rescue 0
949
+ end
888
950
  else
889
951
  value_input.text
890
952
  end.to_s
@@ -908,15 +970,18 @@ module JSON
908
970
  )
909
971
  hbox = HBox.new(false, 5)
910
972
 
911
- hbox.add(Label.new("Order:"))
973
+ hbox.pack_start(Label.new("Order:"), false)
912
974
  hbox.pack_start(order_input = Entry.new)
913
975
  order_input.text = @order || 'x'
976
+ order_input.width_chars = 60
914
977
 
915
- hbox.pack_start(reverse_checkbox = CheckButton.new('Reverse'))
978
+ hbox.pack_start(reverse_checkbox = CheckButton.new('Reverse'), false)
916
979
 
917
- dialog.vbox.add(hbox)
980
+ dialog.vbox.pack_start(hbox, false)
918
981
 
982
+ dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
919
983
  dialog.show_all
984
+ self.focus = dialog
920
985
  dialog.run do |response|
921
986
  if response == Dialog::RESPONSE_ACCEPT
922
987
  return @order = order_input.text, reverse_checkbox.active?
@@ -929,7 +994,7 @@ module JSON
929
994
 
930
995
  # Ask for a find term to search for in the tree. Returns the term as a
931
996
  # string.
932
- def ask_for_find_term
997
+ def ask_for_find_term(search = nil)
933
998
  dialog = Dialog.new(
934
999
  "Find a node matching regex in tree.",
935
1000
  nil, nil,
@@ -938,16 +1003,28 @@ module JSON
938
1003
  )
939
1004
  hbox = HBox.new(false, 5)
940
1005
 
941
- hbox.add(Label.new("Regex:"))
1006
+ hbox.pack_start(Label.new("Regex:"), false)
942
1007
  hbox.pack_start(regex_input = Entry.new)
943
- regex_input.text = @regex || ''
1008
+ hbox.pack_start(icase_checkbox = CheckButton.new('Icase'), false)
1009
+ regex_input.width_chars = 60
1010
+ if search
1011
+ regex_input.text = search.source
1012
+ icase_checkbox.active = search.casefold?
1013
+ end
944
1014
 
945
- dialog.vbox.add(hbox)
1015
+ dialog.vbox.pack_start(hbox, false)
946
1016
 
1017
+ dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
947
1018
  dialog.show_all
1019
+ self.focus = dialog
948
1020
  dialog.run do |response|
949
1021
  if response == Dialog::RESPONSE_ACCEPT
950
- return @regex = regex_input.text
1022
+ begin
1023
+ return Regexp.new(regex_input.text, icase_checkbox.active? ? Regexp::IGNORECASE : 0)
1024
+ rescue => e
1025
+ Editor.error_dialog(self, "Evaluation of regex /#{regex_input.text}/ failed: #{e}!")
1026
+ return
1027
+ end
951
1028
  end
952
1029
  end
953
1030
  return
@@ -1004,6 +1081,18 @@ module JSON
1004
1081
  data = read_data(@filename)
1005
1082
  view_new_model Editor.data2model(data)
1006
1083
  end
1084
+
1085
+ signal_connect(:button_release_event) do |_,event|
1086
+ if event.button == 2
1087
+ c = Gtk::Clipboard.get(Gdk::Selection::PRIMARY)
1088
+ if url = c.wait_for_text
1089
+ location_open url
1090
+ end
1091
+ false
1092
+ else
1093
+ true
1094
+ end
1095
+ end
1007
1096
  end
1008
1097
 
1009
1098
  # Creates the menu bar with the pulldown menus and returns it.
@@ -1063,9 +1152,11 @@ module JSON
1063
1152
  # Quit this editor, that is, leave this editor's main loop.
1064
1153
  def quit
1065
1154
  ask_save if @changed
1066
- destroy
1067
- Gtk.main_quit
1068
- true
1155
+ if Gtk.main_level > 0
1156
+ destroy
1157
+ Gtk.main_quit
1158
+ end
1159
+ nil
1069
1160
  end
1070
1161
 
1071
1162
  # Display the new title according to the editor's current state.
@@ -1083,6 +1174,22 @@ module JSON
1083
1174
  self.view_new_model nil
1084
1175
  end
1085
1176
 
1177
+ def check_pretty_printed(json)
1178
+ pretty = !!((nl_index = json.index("\n")) && nl_index != json.size - 1)
1179
+ @options_menu.pretty_item.active = pretty
1180
+ end
1181
+ private :check_pretty_printed
1182
+
1183
+ # Open the data at the location _uri_, if given. Otherwise open a dialog
1184
+ # to ask for the _uri_.
1185
+ def location_open(uri = nil)
1186
+ uri = ask_for_location unless uri
1187
+ uri or return
1188
+ ask_save if @changed
1189
+ data = load_location(uri) or return
1190
+ view_new_model Editor.data2model(data)
1191
+ end
1192
+
1086
1193
  # Open the file _filename_ or call the #select_file method to ask for a
1087
1194
  # filename.
1088
1195
  def file_open(filename = nil)
@@ -1091,6 +1198,15 @@ module JSON
1091
1198
  view_new_model Editor.data2model(data)
1092
1199
  end
1093
1200
 
1201
+ # Edit the string _json_ in the editor.
1202
+ def edit(json)
1203
+ if json.respond_to? :read
1204
+ json = json.read
1205
+ end
1206
+ data = parse_json json
1207
+ view_new_model Editor.data2model(data)
1208
+ end
1209
+
1094
1210
  # Save the current file.
1095
1211
  def file_save
1096
1212
  if @filename
@@ -1111,10 +1227,11 @@ module JSON
1111
1227
  if path
1112
1228
  data = Editor.model2data(@treeview.model.iter_first)
1113
1229
  File.open(path + '.tmp', 'wb') do |output|
1230
+ data or break
1114
1231
  if @options_menu.pretty_item.active?
1115
- output.puts JSON.pretty_generate(data)
1232
+ output.puts JSON.pretty_generate(data, :max_nesting => false)
1116
1233
  else
1117
- output.write JSON.unparse(data)
1234
+ output.write JSON.generate(data, :max_nesting => false)
1118
1235
  end
1119
1236
  end
1120
1237
  File.rename path + '.tmp', path
@@ -1131,49 +1248,62 @@ module JSON
1131
1248
  if filename
1132
1249
  if File.directory?(filename)
1133
1250
  Editor.error_dialog(self, "Try to select a JSON file!")
1134
- return
1251
+ nil
1135
1252
  else
1136
- data = read_data(filename)
1137
1253
  @filename = filename
1138
- toplevel.display_status("Loaded data from '#@filename'.")
1254
+ if data = read_data(filename)
1255
+ toplevel.display_status("Loaded data from '#@filename'.")
1256
+ end
1139
1257
  display_title
1140
- return data
1258
+ data
1141
1259
  end
1142
1260
  end
1143
1261
  end
1144
1262
 
1145
- def check_pretty_printed(json)
1146
- pretty = !!((nl_index = json.index("\n")) && nl_index != json.size - 1)
1147
- @options_menu.pretty_item.active = pretty
1263
+ # Load the data at location _uri_ into the editor as a JSON document.
1264
+ def load_location(uri)
1265
+ data = read_data(uri) or return
1266
+ @filename = nil
1267
+ toplevel.display_status("Loaded data from '#{uri}'.")
1268
+ display_title
1269
+ data
1148
1270
  end
1149
- private :check_pretty_printed
1150
1271
 
1151
- # Read a JSON document from the file named _filename_, parse it into a
1152
- # ruby data structure, and return the data.
1153
- def read_data(filename)
1154
- json = File.read(filename)
1272
+ def parse_json(json)
1155
1273
  check_pretty_printed(json)
1156
1274
  if @encoding && !/^utf8$/i.match(@encoding)
1157
1275
  iconverter = Iconv.new('utf8', @encoding)
1158
1276
  json = iconverter.iconv(json)
1159
1277
  end
1160
- JSON::parse(json)
1161
- rescue JSON::JSONError => e
1278
+ JSON::parse(json, :max_nesting => false, :create_additions => false)
1279
+ end
1280
+ private :parse_json
1281
+
1282
+ # Read a JSON document from the file named _filename_, parse it into a
1283
+ # ruby data structure, and return the data.
1284
+ def read_data(filename)
1285
+ open(filename) do |f|
1286
+ json = f.read
1287
+ return parse_json(json)
1288
+ end
1289
+ rescue => e
1162
1290
  Editor.error_dialog(self, "Failed to parse JSON file: #{e}!")
1163
1291
  return
1164
- rescue SystemCallError => e
1165
- quit
1166
1292
  end
1167
1293
 
1168
1294
  # Open a file selecton dialog, displaying _message_, and return the
1169
1295
  # selected filename or nil, if no file was selected.
1170
1296
  def select_file(message)
1171
1297
  filename = nil
1172
- fs = FileSelection.new(message).set_modal(true).
1173
- set_filename(Dir.pwd + "/").set_transient_for(self)
1298
+ fs = FileSelection.new(message)
1299
+ fs.set_modal(true)
1300
+ @default_dir = File.join(Dir.pwd, '') unless @default_dir
1301
+ fs.set_filename(@default_dir)
1302
+ fs.set_transient_for(self)
1174
1303
  fs.signal_connect(:destroy) { Gtk.main_quit }
1175
1304
  fs.ok_button.signal_connect(:clicked) do
1176
1305
  filename = fs.filename
1306
+ @default_dir = File.join(File.dirname(filename), '')
1177
1307
  fs.destroy
1178
1308
  Gtk.main_quit
1179
1309
  end
@@ -1185,13 +1315,41 @@ module JSON
1185
1315
  Gtk.main
1186
1316
  filename
1187
1317
  end
1318
+
1319
+ # Ask for location URI a to load data from. Returns the URI as a string.
1320
+ def ask_for_location
1321
+ dialog = Dialog.new(
1322
+ "Load data from location...",
1323
+ nil, nil,
1324
+ [ Stock::OK, Dialog::RESPONSE_ACCEPT ],
1325
+ [ Stock::CANCEL, Dialog::RESPONSE_REJECT ]
1326
+ )
1327
+ hbox = HBox.new(false, 5)
1328
+
1329
+ hbox.pack_start(Label.new("Location:"), false)
1330
+ hbox.pack_start(location_input = Entry.new)
1331
+ location_input.width_chars = 60
1332
+ location_input.text = @location || ''
1333
+
1334
+ dialog.vbox.pack_start(hbox, false)
1335
+
1336
+ dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
1337
+ dialog.show_all
1338
+ dialog.run do |response|
1339
+ if response == Dialog::RESPONSE_ACCEPT
1340
+ return @location = location_input.text
1341
+ end
1342
+ end
1343
+ return
1344
+ ensure
1345
+ dialog.destroy if dialog
1346
+ end
1188
1347
  end
1189
1348
 
1190
1349
  class << self
1191
1350
  # Starts a JSON Editor. If a block was given, it yields
1192
1351
  # to the JSON::Editor::MainWindow instance.
1193
- def start(encoding = nil) # :yield: window
1194
- encoding ||= 'utf8'
1352
+ def start(encoding = 'utf8') # :yield: window
1195
1353
  Gtk.init
1196
1354
  @window = Editor::MainWindow.new(encoding)
1197
1355
  @window.icon_list = [ Editor.fetch_icon('json') ]
@@ -1200,8 +1358,14 @@ module JSON
1200
1358
  Gtk.main
1201
1359
  end
1202
1360
 
1361
+ # Edit the string _json_ with encoding _encoding_ in the editor.
1362
+ def edit(json, encoding = 'utf8')
1363
+ start(encoding) do |window|
1364
+ window.edit json
1365
+ end
1366
+ end
1367
+
1203
1368
  attr_reader :window
1204
1369
  end
1205
1370
  end
1206
1371
  end
1207
- # vim: set et sw=2 ts=2:
data/lib/json/ext.rb CHANGED
@@ -10,4 +10,6 @@ module JSON
10
10
  JSON.parser = Parser
11
11
  JSON.generator = Generator
12
12
  end
13
+
14
+ JSON_LOADED = true
13
15
  end