rroonga 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/NEWS.ja.rdoc +18 -0
  2. data/NEWS.rdoc +18 -0
  3. data/bin/grndump +71 -0
  4. data/ext/groonga/rb-grn-accessor.c +64 -1
  5. data/ext/groonga/rb-grn-context.c +40 -1
  6. data/ext/groonga/rb-grn-database.c +47 -5
  7. data/ext/groonga/rb-grn-index-column.c +77 -7
  8. data/ext/groonga/rb-grn-object.c +8 -4
  9. data/ext/groonga/rb-grn-table-cursor.c +56 -1
  10. data/ext/groonga/rb-grn-table-key-support.c +2 -2
  11. data/ext/groonga/rb-grn-table.c +10 -38
  12. data/ext/groonga/rb-grn.h +11 -1
  13. data/html/index.html +6 -6
  14. data/html/ranguba.css +8 -1
  15. data/lib/groonga.rb +1 -0
  16. data/lib/groonga/dumper.rb +481 -0
  17. data/lib/groonga/schema.rb +54 -181
  18. data/rroonga-build.rb +1 -1
  19. data/test-unit-notify/Rakefile +47 -0
  20. data/test-unit-notify/lib/test/unit/notify.rb +104 -0
  21. data/test-unit/COPYING +56 -0
  22. data/test-unit/GPL +340 -0
  23. data/test-unit/PSFL +271 -0
  24. data/test-unit/Rakefile +18 -5
  25. data/test-unit/html/bar.svg +153 -0
  26. data/test-unit/html/developer.svg +469 -0
  27. data/test-unit/html/favicon.ico +0 -0
  28. data/test-unit/html/favicon.svg +82 -0
  29. data/test-unit/html/heading-mark.svg +393 -0
  30. data/test-unit/html/index.html +235 -13
  31. data/test-unit/html/index.html.ja +258 -15
  32. data/test-unit/html/install.svg +636 -0
  33. data/test-unit/html/logo.svg +483 -0
  34. data/test-unit/html/test-unit.css +339 -0
  35. data/test-unit/html/tutorial.svg +559 -0
  36. data/test-unit/lib/test/unit.rb +6 -1
  37. data/test-unit/lib/test/unit/assertions.rb +115 -11
  38. data/test-unit/lib/test/unit/autorunner.rb +5 -2
  39. data/test-unit/lib/test/unit/collector/load.rb +1 -1
  40. data/test-unit/lib/test/unit/color-scheme.rb +6 -2
  41. data/test-unit/lib/test/unit/diff.rb +17 -1
  42. data/test-unit/lib/test/unit/testcase.rb +7 -0
  43. data/test-unit/lib/test/unit/testresult.rb +34 -2
  44. data/test-unit/lib/test/unit/ui/console/testrunner.rb +9 -45
  45. data/test-unit/lib/test/unit/ui/tap/testrunner.rb +2 -12
  46. data/test-unit/lib/test/unit/ui/testrunner.rb +25 -0
  47. data/test-unit/lib/test/unit/util/backtracefilter.rb +1 -0
  48. data/test-unit/lib/test/unit/util/output.rb +31 -0
  49. data/test-unit/lib/test/unit/version.rb +1 -1
  50. data/test-unit/test/test-color-scheme.rb +4 -2
  51. data/test-unit/test/test_assertions.rb +51 -5
  52. data/test-unit/test/ui/test_tap.rb +33 -0
  53. data/test-unit/test/util/test-output.rb +11 -0
  54. data/test/groonga-test-utils.rb +1 -0
  55. data/test/test-accessor.rb +32 -0
  56. data/test/test-context.rb +7 -1
  57. data/test/test-database-dumper.rb +156 -0
  58. data/test/test-index-column.rb +67 -1
  59. data/test/test-schema-dumper.rb +181 -0
  60. data/test/test-schema.rb +53 -97
  61. data/test/test-table-dumper.rb +83 -0
  62. metadata +48 -11
  63. data/ext/groonga/mkmf.log +0 -99
  64. data/test-unit/html/classic.html +0 -15
@@ -133,8 +133,8 @@ rb_grn_object_finalizer (grn_ctx *context, int n_args, grn_obj **grn_objects,
133
133
  RB_GRN_INDEX_COLUMN(rb_grn_object));
134
134
  break;
135
135
  case GRN_ACCESSOR:
136
- rb_grn_named_object_finalizer(context, grn_object,
137
- RB_GRN_NAMED_OBJECT(rb_grn_object));
136
+ rb_grn_accessor_finalizer(context, grn_object,
137
+ RB_GRN_ACCESSOR(rb_grn_object));
138
138
  break;
139
139
  case GRN_EXPR:
140
140
  rb_grn_expression_finalizer(context, grn_object,
@@ -378,8 +378,12 @@ rb_grn_object_assign (VALUE klass, VALUE self, VALUE rb_context,
378
378
  rb_grn_object_bind_common(klass, self, rb_context, rb_grn_object,
379
379
  context, object);
380
380
  rb_grn_column_bind(RB_GRN_COLUMN(rb_grn_object), context, object);
381
- } else if (klass == rb_cGrnAccessor ||
382
- klass == rb_cGrnViewAccessor) {
381
+ } else if (klass == rb_cGrnAccessor) {
382
+ rb_grn_object = ALLOC(RbGrnAccessor);
383
+ rb_grn_object_bind_common(klass, self, rb_context, rb_grn_object,
384
+ context, object);
385
+ rb_grn_accessor_bind(RB_GRN_ACCESSOR(rb_grn_object), context, object);
386
+ } else if (klass == rb_cGrnViewAccessor) {
383
387
  rb_grn_object = ALLOC(RbGrnNamedObject);
384
388
  rb_grn_object_bind_common(klass, self, rb_context, rb_grn_object,
385
389
  context, object);
@@ -1,6 +1,6 @@
1
1
  /* -*- c-file-style: "ruby" -*- */
2
2
  /*
3
- Copyright (C) 2009-2010 Kouhei Sutou <kou@clear-code.com>
3
+ Copyright (C) 2009-2011 Kouhei Sutou <kou@clear-code.com>
4
4
 
5
5
  This library is free software; you can redistribute it and/or
6
6
  modify it under the terms of the GNU Lesser General Public
@@ -64,6 +64,61 @@ rb_grn_table_cursor_deconstruct (RbGrnTableCursor *rb_grn_table_cursor,
64
64
  range_id, range);
65
65
  }
66
66
 
67
+ int
68
+ rb_grn_table_cursor_order_to_flag (VALUE rb_order)
69
+ {
70
+ int flag = 0;
71
+
72
+ if (NIL_P(rb_order) ||
73
+ rb_grn_equal_option(rb_order, "asc") ||
74
+ rb_grn_equal_option(rb_order, "ascending")) {
75
+ flag |= GRN_CURSOR_ASCENDING;
76
+ } else if (rb_grn_equal_option(rb_order, "desc") ||
77
+ rb_grn_equal_option(rb_order, "descending")) {
78
+ flag |= GRN_CURSOR_DESCENDING;
79
+ } else {
80
+ rb_raise(rb_eArgError,
81
+ "order should be one of "
82
+ "[:asc, :ascending, :desc, :descending]: %s",
83
+ rb_grn_inspect(rb_order));
84
+ }
85
+
86
+ return flag;
87
+ }
88
+
89
+ int
90
+ rb_grn_table_cursor_order_by_to_flag (unsigned char table_type,
91
+ VALUE rb_table,
92
+ VALUE rb_order_by)
93
+ {
94
+ int flag = 0;
95
+
96
+ if (NIL_P(rb_order_by)) {
97
+ if (table_type == GRN_TABLE_PAT_KEY) {
98
+ flag |= GRN_CURSOR_BY_KEY;
99
+ } else {
100
+ flag |= GRN_CURSOR_BY_ID;
101
+ }
102
+ } else if (rb_grn_equal_option(rb_order_by, "id")) {
103
+ flag |= GRN_CURSOR_BY_ID;
104
+ } else if (rb_grn_equal_option(rb_order_by, "key")) {
105
+ if (table_type != GRN_TABLE_PAT_KEY) {
106
+ rb_raise(rb_eArgError,
107
+ "order_by => :key is available "
108
+ "only for Groonga::PatriciaTrie: %s",
109
+ rb_grn_inspect(rb_table));
110
+ }
111
+ flag |= GRN_CURSOR_BY_KEY;
112
+ } else {
113
+ rb_raise(rb_eArgError,
114
+ "order_by should be one of [:id%s]: %s",
115
+ table_type == GRN_TABLE_PAT_KEY ? ", :key" : "",
116
+ rb_grn_inspect(rb_order_by));
117
+ }
118
+
119
+ return flag;
120
+ }
121
+
67
122
  /*
68
123
  * call-seq:
69
124
  * cursor.value -> 値
@@ -663,7 +663,7 @@ rb_grn_table_key_support_set_default_tokenizer (VALUE self, VALUE rb_tokenizer)
663
663
  * す。
664
664
  */
665
665
  static VALUE
666
- rb_grn_table_key_support_normalize_key_p (VALUE self)
666
+ rb_grn_table_key_normalize_key_p (VALUE self)
667
667
  {
668
668
  grn_obj *table;
669
669
 
@@ -732,7 +732,7 @@ rb_grn_init_table_key_support (VALUE mGrn)
732
732
  rb_grn_table_key_support_set_default_tokenizer, 1);
733
733
 
734
734
  rb_define_method(rb_mGrnTableKeySupport, "normalize_key?",
735
- rb_grn_table_key_support_normalize_key_p, 0);
735
+ rb_grn_table_key_normalize_key_p, 0);
736
736
 
737
737
  rb_define_method(rb_mGrnTableKeySupport, "support_key?",
738
738
  rb_grn_table_key_support_support_key_p, 0);
@@ -1,6 +1,6 @@
1
1
  /* -*- c-file-style: "ruby" -*- */
2
2
  /*
3
- Copyright (C) 2009-2010 Kouhei Sutou <kou@clear-code.com>
3
+ Copyright (C) 2009-2011 Kouhei Sutou <kou@clear-code.com>
4
4
 
5
5
  This library is free software; you can redistribute it and/or
6
6
  modify it under the terms of the GNU Lesser General Public
@@ -43,7 +43,9 @@ grn_obj *
43
43
  rb_grn_table_from_ruby_object (VALUE object, grn_ctx **context)
44
44
  {
45
45
  if (!RVAL2CBOOL(rb_obj_is_kind_of(object, rb_cGrnTable))) {
46
- rb_raise(rb_eTypeError, "not a groonga table");
46
+ rb_raise(rb_eTypeError,
47
+ "not a groonga table: <%s>",
48
+ rb_grn_inspect(object));
47
49
  }
48
50
 
49
51
  return RVAL2GRNOBJECT(object, context);
@@ -738,41 +740,10 @@ rb_grn_table_open_grn_cursor (int argc, VALUE *argv, VALUE self,
738
740
  if (!NIL_P(rb_limit))
739
741
  limit = NUM2INT(rb_limit);
740
742
 
741
- if (NIL_P(rb_order)) {
742
- } else if (rb_grn_equal_option(rb_order, "asc") ||
743
- rb_grn_equal_option(rb_order, "ascending")) {
744
- flags |= GRN_CURSOR_ASCENDING;
745
- } else if (rb_grn_equal_option(rb_order, "desc") ||
746
- rb_grn_equal_option(rb_order, "descending")) {
747
- flags |= GRN_CURSOR_DESCENDING;
748
- } else {
749
- rb_raise(rb_eArgError,
750
- "order should be one of "
751
- "[:asc, :ascending, :desc, :descending]: %s",
752
- rb_grn_inspect(rb_order));
753
- }
754
- if (NIL_P(rb_order_by)) {
755
- if (table->header.type == GRN_TABLE_PAT_KEY) {
756
- flags |= GRN_CURSOR_BY_KEY;
757
- } else {
758
- flags |= GRN_CURSOR_BY_ID;
759
- }
760
- } else if (rb_grn_equal_option(rb_order_by, "id")) {
761
- flags |= GRN_CURSOR_BY_ID;
762
- } else if (rb_grn_equal_option(rb_order_by, "key")) {
763
- if (table->header.type != GRN_TABLE_PAT_KEY) {
764
- rb_raise(rb_eArgError,
765
- "order_by => :key is available "
766
- "only for Groonga::PatriciaTrie: %s",
767
- rb_grn_inspect(self));
768
- }
769
- flags |= GRN_CURSOR_BY_KEY;
770
- } else {
771
- rb_raise(rb_eArgError,
772
- "order_by should be one of [:id%s]: %s",
773
- table->header.type == GRN_TABLE_PAT_KEY ? ", :key" : "",
774
- rb_grn_inspect(rb_order_by));
775
- }
743
+ flags |= rb_grn_table_cursor_order_to_flag(rb_order);
744
+ flags |= rb_grn_table_cursor_order_by_to_flag(table->header.type,
745
+ self,
746
+ rb_order_by);
776
747
 
777
748
  if (RVAL2CBOOL(rb_greater_than))
778
749
  flags |= GRN_CURSOR_GT;
@@ -816,7 +787,8 @@ rb_grn_table_open_grn_cursor (int argc, VALUE *argv, VALUE self,
816
787
  *
817
788
  * [+:order+]
818
789
  * +:asc+または+:ascending+を指定すると昇順にレコードを取
819
- * り出す。
790
+ * り出す。(デフォルト)
791
+ *
820
792
  * +:desc+または+:descending+を指定すると降順にレコードを
821
793
  * 取り出す。
822
794
  *
data/ext/groonga/rb-grn.h CHANGED
@@ -68,7 +68,7 @@ RB_GRN_BEGIN_DECLS
68
68
  #endif
69
69
 
70
70
  #define RB_GRN_MAJOR_VERSION 1
71
- #define RB_GRN_MINOR_VERSION 1
71
+ #define RB_GRN_MINOR_VERSION 2
72
72
  #define RB_GRN_MICRO_VERSION 0
73
73
 
74
74
  #define RB_GRN_QUERY_DEFAULT_MAX_EXPRESSIONS 32
@@ -153,6 +153,7 @@ typedef struct _RbGrnAccessor RbGrnAccessor;
153
153
  struct _RbGrnAccessor
154
154
  {
155
155
  RbGrnNamedObject parent;
156
+ grn_obj *value;
156
157
  };
157
158
 
158
159
  typedef struct _RbGrnTableCursor RbGrnTableCursor;
@@ -386,6 +387,10 @@ VALUE rb_grn_table_set_column_value (VALUE self,
386
387
 
387
388
  grn_ctx *rb_grn_table_cursor_ensure_context (VALUE cursor,
388
389
  VALUE *rb_context);
390
+ int rb_grn_table_cursor_order_to_flag (VALUE rb_order);
391
+ int rb_grn_table_cursor_order_by_to_flag (unsigned char table_type,
392
+ VALUE rb_table,
393
+ VALUE rb_order_by);
389
394
 
390
395
  void rb_grn_table_key_support_bind (RbGrnTableKeySupport *rb_grn_table_key_support,
391
396
  grn_ctx *context,
@@ -518,6 +523,11 @@ VALUE rb_grn_column_expression_builder_build
518
523
  #define GRNCOLUMN2RVAL(klass, context, column, owner) \
519
524
  (rb_grn_column_to_ruby_object(klass, context, column, owner))
520
525
 
526
+ #define RVAL2GRNACCESSOR(object) \
527
+ (rb_grn_accessor_from_ruby_object(object))
528
+ #define GRNACCESSOR2RVAL(context, accessor, owner) \
529
+ (rb_grn_accessor_to_ruby_object(context, accessor, owner))
530
+
521
531
  #define RVAL2GRNQUERY(object) (rb_grn_query_from_ruby_object(object))
522
532
  #define GRNQUERY2RVAL(context, column)(rb_grn_query_to_ruby_object(context, column))
523
533
 
data/html/index.html CHANGED
@@ -49,7 +49,7 @@
49
49
 
50
50
  <h3>rroongaの最新リリース</h3>
51
51
  <p>
52
- 2011-02-09にリリースされた1.1.0が最新です。
52
+ 2011-04-01にリリースされた1.2.0が最新です。
53
53
  </p>
54
54
 
55
55
  <h3 id="install-rroonga">rroongaのインストール</h3>
@@ -100,7 +100,7 @@
100
100
  これから徐々に追加されていく予定です。
101
101
  </p>
102
102
  <ul>
103
- <li><a href="active_groonga/">ActiveGroongaのリファレンスマニュアル</a></li>
103
+ <li><a href="activegroonga/">ActiveGroongaのリファレンスマニュアル</a></li>
104
104
  </ul>
105
105
 
106
106
  <h2 id="about-racknga">racknga</h2>
@@ -135,11 +135,11 @@
135
135
 
136
136
  <h3>ChupaTextの最新リリース</h3>
137
137
  <p>
138
- 2010-12-26にリリースされた0.8.0が最新です。
138
+ 2011-02-09にリリースされた0.9.0が最新です。
139
139
  </p>
140
140
  <p>
141
- [<a href="http://rubyforge.org/frs/download.php/73718/chupatext-0.8.0.tar.gz">ダウンロード</a>]
142
- [<a href="chupatext/ja/news.html#news.release-0-8-0">変更点</a>]
141
+ [<a href="http://rubyforge.org/frs/download.php/74190/chupatext-0.9.0.tar.gz">ダウンロード</a>]
142
+ [<a href="chupatext/ja/news.html#news.release-0-9-0">変更点</a>]
143
143
  </p>
144
144
 
145
145
  <h3 id="install-chupatext">ChupaTextのインストール</h3>
@@ -216,7 +216,7 @@
216
216
  </p>
217
217
  <p id="sponsor-github">
218
218
  <a href="http://github.com/ranguba/">
219
- ラングバプロジェクトはGitHubにホスティングしてもらっています。
219
+ <img src="github-logo.png" width="100" height="45" border="0" alt="ラングバプロジェクトはGitHubにホスティングしてもらっています。" />
220
220
  </a>
221
221
  </p>
222
222
  <p id="sponsor-tango">
data/html/ranguba.css CHANGED
@@ -248,13 +248,20 @@ div.sponsors p#sponsor-rubyforge
248
248
  right: 50px;
249
249
  }
250
250
 
251
- div.sponsors p#sponsor-tango
251
+ div.sponsors p#sponsor-github
252
252
  {
253
253
  position: absolute;
254
254
  top: 50px;
255
255
  right: 50px;
256
256
  }
257
257
 
258
+ div.sponsors p#sponsor-tango
259
+ {
260
+ position: absolute;
261
+ top: 105px;
262
+ right: 50px;
263
+ }
264
+
258
265
  dt
259
266
  {
260
267
  margin-bottom: 0.25em;
data/lib/groonga.rb CHANGED
@@ -87,5 +87,6 @@ end
87
87
 
88
88
  require 'groonga/context'
89
89
  require 'groonga/patricia-trie'
90
+ require 'groonga/dumper'
90
91
  require 'groonga/schema'
91
92
  require 'groonga/pagination'
@@ -0,0 +1,481 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2011 Kouhei Sutou <kou@clear-code.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require 'stringio'
19
+
20
+ module Groonga
21
+ # データベースの内容をgrn式形式の文字列として出力するクラス。
22
+ class DatabaseDumper
23
+ def initialize(options={})
24
+ @options = options
25
+ end
26
+
27
+ def dump
28
+ options = @options.dup
29
+ have_output = !@options[:output].nil?
30
+ options[:output] ||= StringIO.new
31
+ if options[:database].nil?
32
+ options[:context] ||= Groonga::Context.default
33
+ options[:database] = options[:context].database
34
+ end
35
+ options[:dump_schema] = true if options[:dump_schema].nil?
36
+ options[:dump_tables] = true if options[:dump_tables].nil?
37
+
38
+ dump_schema(options) if options[:dump_schema]
39
+ dump_tables(options) if options[:dump_tables]
40
+
41
+ if have_output
42
+ nil
43
+ else
44
+ options[:output].string
45
+ end
46
+ end
47
+
48
+ private
49
+ def dump_schema(options)
50
+ SchemaDumper.new(options.merge(:syntax => :command)).dump
51
+ end
52
+
53
+ def dump_tables(options)
54
+ first_table = true
55
+ options[:database].each(:order_by => :key) do |object|
56
+ next unless object.is_a?(Groonga::Table)
57
+ next if object.size.zero?
58
+ next if lexicon_table?(object)
59
+ next unless target_table?(options[:tables], object)
60
+ options[:output].write("\n") if !first_table or options[:dump_schema]
61
+ first_table = false
62
+ dump_records(object, options)
63
+ end
64
+ end
65
+
66
+ def dump_records(table, options)
67
+ TableDumper.new(table, options).dump
68
+ end
69
+
70
+ def lexicon_table?(table)
71
+ table.support_key? and
72
+ table.default_tokenizer and
73
+ table.columns.any? {|column| column.index?}
74
+ end
75
+
76
+ def target_table?(target_tables, table)
77
+ return true if target_tables.nil? or target_tables.empty?
78
+ target_tables.any? do |name|
79
+ name === table.name
80
+ end
81
+ end
82
+ end
83
+
84
+ # スキーマの内容をRubyスクリプトまたはgrn式形式の文字列と
85
+ # して出力するクラス。
86
+ class SchemaDumper
87
+ def initialize(options={})
88
+ @options = (options || {}).dup
89
+ end
90
+
91
+ def dump
92
+ database = @options[:database]
93
+ if database.nil?
94
+ context = @options[:context] || Groonga::Context.default
95
+ database = context.database
96
+ end
97
+ return nil if database.nil?
98
+
99
+ output = @options[:output]
100
+ have_output = !output.nil?
101
+ output ||= StringIO.new
102
+ result = syntax(database, output).dump
103
+ if have_output
104
+ result
105
+ else
106
+ output.string
107
+ end
108
+ end
109
+
110
+ private
111
+ def syntax(database, output)
112
+ case @options[:syntax]
113
+ when :command
114
+ CommandSyntax.new(database, output)
115
+ else
116
+ RubySyntax.new(database, output)
117
+ end
118
+ end
119
+
120
+ class BaseSyntax # :nodoc:
121
+ def initialize(database, output)
122
+ @database = database
123
+ @output = output
124
+ @table_defined = false
125
+ @index_columns = []
126
+ @reference_columns = []
127
+ end
128
+
129
+ def dump
130
+ header
131
+ dump_schema
132
+ footer
133
+ end
134
+
135
+ def dump_schema
136
+ @database.each(:order_by => :key) do |object|
137
+ create_table(object) if object.is_a?(Groonga::Table)
138
+ end
139
+
140
+ @reference_columns.group_by do |column|
141
+ column.table
142
+ end.each do |table, columns|
143
+ change_table(table) do
144
+ columns.each do |column|
145
+ define_reference_column(table, column)
146
+ end
147
+ end
148
+ end
149
+
150
+ @index_columns.group_by do |column|
151
+ column.table
152
+ end.each do |table, columns|
153
+ change_table(table) do
154
+ columns.each do |column|
155
+ define_index_column(table, column)
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ private
162
+ def write(content)
163
+ @output.write(content)
164
+ end
165
+
166
+ def header
167
+ write("")
168
+ end
169
+
170
+ def footer
171
+ write("")
172
+ end
173
+
174
+ def table_separator
175
+ write("\n")
176
+ end
177
+
178
+ def create_table(table)
179
+ table_separator if @table_defined
180
+ create_table_header(table)
181
+ table.columns.sort_by {|column| column.local_name}.each do |column|
182
+ if column.is_a?(Groonga::IndexColumn)
183
+ @index_columns << column
184
+ else
185
+ if column.range.is_a?(Groonga::Table)
186
+ @reference_columns << column
187
+ else
188
+ define_column(table, column)
189
+ end
190
+ end
191
+ end
192
+ create_table_footer(table)
193
+ @table_defined = true
194
+ end
195
+
196
+ def change_table(table)
197
+ table_separator if @table_defined
198
+ change_table_header(table)
199
+ yield(table)
200
+ change_table_footer(table)
201
+ @table_defined = true
202
+ end
203
+ end
204
+
205
+ class RubySyntax < BaseSyntax # :nodoc:
206
+ private
207
+ def create_table_header(table)
208
+ parameters = []
209
+ unless table.is_a?(Groonga::Array)
210
+ case table
211
+ when Groonga::Hash
212
+ parameters << ":type => :hash"
213
+ when Groonga::PatriciaTrie
214
+ parameters << ":type => :patricia_trie"
215
+ end
216
+ if table.domain
217
+ parameters << ":key_type => #{table.domain.name.dump}"
218
+ if table.normalize_key?
219
+ parameters << ":key_normalize => true"
220
+ end
221
+ end
222
+ default_tokenizer = table.default_tokenizer
223
+ if default_tokenizer
224
+ parameters << ":default_tokenizer => #{default_tokenizer.name.dump}"
225
+ end
226
+ end
227
+ parameters << ":force => true"
228
+ parameters.unshift("")
229
+ parameters = parameters.join(",\n ")
230
+ write("create_table(#{table.name.dump}#{parameters}) do |table|\n")
231
+ end
232
+
233
+ def create_table_footer(table)
234
+ write("end\n")
235
+ end
236
+
237
+ def change_table_header(table)
238
+ write("change_table(#{table.name.inspect}) do |table|\n")
239
+ end
240
+
241
+ def change_table_footer(table)
242
+ write("end\n")
243
+ end
244
+
245
+ def define_column(table, column)
246
+ type = column_method(column)
247
+ name = column.local_name
248
+ write(" table.#{type}(#{name.inspect})\n")
249
+ end
250
+
251
+ def define_reference_column(table, column)
252
+ name = column.local_name
253
+ reference = column.range
254
+ write(" table.reference(#{name.dump}, #{reference.name.dump})\n")
255
+ end
256
+
257
+ def define_index_column(table, column)
258
+ target_table_name = column.range.name
259
+ sources = column.sources
260
+ source_names = sources.collect do |source|
261
+ if source.is_a?(table.class)
262
+ "_key".dump
263
+ else
264
+ source.local_name.dump
265
+ end
266
+ end.join(", ")
267
+ arguments = [target_table_name.dump,
268
+ sources.size == 1 ? source_names : "[#{source_names}]",
269
+ ":name => #{column.local_name.dump}"]
270
+ write(" table.index(#{arguments.join(', ')})\n")
271
+ end
272
+
273
+ def column_method(column)
274
+ range = column.range
275
+ case range.name
276
+ when "Int32"
277
+ "integer32"
278
+ when "Int64"
279
+ "integer64"
280
+ when "UInt32"
281
+ "unsigned_integer32"
282
+ when "UInt64"
283
+ "unsigned_integer64"
284
+ when "Float"
285
+ "float"
286
+ when "Time"
287
+ "time"
288
+ when "ShortText"
289
+ "short_text"
290
+ when "Text"
291
+ "text"
292
+ when "LongText"
293
+ "long_text"
294
+ else
295
+ raise ArgumentError, "unsupported column: #{column.inspect}"
296
+ end
297
+ end
298
+ end
299
+
300
+ class CommandSyntax < BaseSyntax # :nodoc:
301
+ private
302
+ def create_table_header(table)
303
+ parameters = []
304
+ flags = []
305
+ case table
306
+ when Groonga::Array
307
+ flags << "TABLE_NO_KEY"
308
+ when Groonga::Hash
309
+ flags << "TABLE_HASH_KEY"
310
+ when Groonga::PatriciaTrie
311
+ flags << "TABLE_PAT_KEY"
312
+ end
313
+ if table.domain
314
+ flags << "KEY_NORMALIZE" if table.normalize_key?
315
+ if table.is_a?(Groonga::PatriciaTrie) and table.register_key_with_sis?
316
+ flags << "KEY_WITH_SIS"
317
+ end
318
+ end
319
+ parameters << "#{flags.join('|')}"
320
+ if table.domain
321
+ parameters << "--key_type #{table.domain.name}"
322
+ end
323
+ if table.range
324
+ parameters << "--value_type #{table.range.name}"
325
+ end
326
+ if table.domain
327
+ default_tokenizer = table.default_tokenizer
328
+ if default_tokenizer
329
+ parameters << "--default_tokenizer #{default_tokenizer.name}"
330
+ end
331
+ end
332
+ write("table_create #{table.name} #{parameters.join(' ')}\n")
333
+ end
334
+
335
+ def create_table_footer(table)
336
+ end
337
+
338
+ def change_table_header(table)
339
+ end
340
+
341
+ def change_table_footer(table)
342
+ end
343
+
344
+ def define_column(table, column)
345
+ parameters = []
346
+ parameters << table.name
347
+ parameters << column.local_name
348
+ flags = []
349
+ if column.scalar?
350
+ flags << "COLUMN_SCALAR"
351
+ elsif column.vector?
352
+ flags << "COLUMN_VECTOR"
353
+ end
354
+ # TODO: support COMPRESS_ZLIB and COMPRESS_LZO?
355
+ parameters << "#{flags.join('|')}"
356
+ parameters << "#{column.range.name}"
357
+ write("column_create #{parameters.join(' ')}\n")
358
+ end
359
+
360
+ def define_reference_column(table, column)
361
+ define_column(table, column)
362
+ end
363
+
364
+ def define_index_column(table, column)
365
+ parameters = []
366
+ parameters << table.name
367
+ parameters << column.local_name
368
+ flags = []
369
+ flags << "COLUMN_INDEX"
370
+ flags << "WITH_SECTION" if column.with_section?
371
+ flags << "WITH_WEIGHT" if column.with_weight?
372
+ flags << "WITH_POSITION" if column.with_position?
373
+ parameters << "#{flags.join('|')}"
374
+ parameters << "#{column.range.name}"
375
+ source_names = column.sources.collect do |source|
376
+ if source.is_a?(table.class)
377
+ "_key"
378
+ else
379
+ source.local_name
380
+ end
381
+ end
382
+ parameters << "#{source_names.join(',')}" unless source_names.empty?
383
+ write("column_create #{parameters.join(' ')}\n")
384
+ end
385
+ end
386
+ end
387
+
388
+ class TableDumper
389
+ def initialize(table, options={})
390
+ @table = table
391
+ @options = options
392
+ @output = @options[:output]
393
+ @have_output = !@output.nil?
394
+ @output ||= StringIO.new
395
+ end
396
+
397
+ def dump
398
+ dump_load_command
399
+ if @have_output
400
+ nil
401
+ else
402
+ @output.string
403
+ end
404
+ end
405
+
406
+ private
407
+ def write(content)
408
+ @output.write(content)
409
+ end
410
+
411
+ def dump_load_command
412
+ write("load --table #{@table.name}\n")
413
+ write("[\n")
414
+ columns = available_columns
415
+ dump_columns(columns)
416
+ dump_records(columns)
417
+ write("\n]\n")
418
+ end
419
+
420
+ def dump_columns(columns)
421
+ column_names = columns.collect do |column|
422
+ column.local_name
423
+ end
424
+ write(column_names.to_json)
425
+ end
426
+
427
+ def dump_records(columns)
428
+ @table.each do |record|
429
+ write(",\n")
430
+ values = columns.collect do |column|
431
+ resolve_value(column[record.id])
432
+ end
433
+ write(values.to_json)
434
+ end
435
+ end
436
+
437
+ def resolve_value(value)
438
+ case value
439
+ when ::Array
440
+ value.collect do |v|
441
+ resolve_value(v)
442
+ end
443
+ when Groonga::Record
444
+ if value.support_key?
445
+ value = value.key
446
+ else
447
+ value = value.id
448
+ end
449
+ resolve_value(value)
450
+ when Time
451
+ # TODO: groonga should support UTC format literal
452
+ # value.utc.strftime("%Y-%m-%d %H:%M:%S.%6N")
453
+ value.to_f
454
+ when NilClass
455
+ # TODO: remove me. nil reference column value
456
+ # doesn't accept null.
457
+ ""
458
+ else
459
+ value
460
+ end
461
+ end
462
+
463
+ def available_columns
464
+ columns = []
465
+ if @table.support_key?
466
+ columns << @table.column("_key")
467
+ else
468
+ columns << @table.column("_id")
469
+ end
470
+ columns << @table.column("_value") unless @table.range.nil?
471
+ data_columns = @table.columns.reject do |column|
472
+ column.index?
473
+ end
474
+ sorted_columns = data_columns.sort_by do |column|
475
+ column.local_name
476
+ end
477
+ columns.concat(sorted_columns)
478
+ columns
479
+ end
480
+ end
481
+ end