activegroonga 0.0.2 → 0.0.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.
- data/NEWS.ja.rdoc +4 -0
- data/NEWS.rdoc +4 -0
- data/README.ja.rdoc +1 -1
- data/README.rdoc +1 -1
- data/Rakefile +3 -4
- data/lib/active_groonga.rb +2 -0
- data/lib/active_groonga/base.rb +125 -99
- data/lib/active_groonga/column.rb +9 -0
- data/lib/active_groonga/dynamic_record_expression_builder.rb +40 -0
- data/lib/active_groonga/schema.rb +107 -204
- data/lib/active_groonga/schema_dumper.rb +40 -25
- data/lib/active_groonga/tasks/groonga.rake +2 -0
- data/lib/active_groonga/version.rb +1 -1
- data/rails_generators/index_table_groonga/USAGE +23 -0
- data/rails_generators/index_table_groonga/index_table_groonga_generator.rb +44 -0
- data/rails_generators/index_table_groonga/templates/migration.rb +12 -0
- data/rails_generators/migration_groonga/USAGE +29 -0
- data/rails_generators/migration_groonga/migration_groonga_generator.rb +19 -0
- data/rails_generators/migration_groonga/templates/migration.rb +11 -0
- data/test-unit/Rakefile +6 -1
- data/test-unit/lib/test/unit/autorunner.rb +26 -3
- data/test-unit/lib/test/unit/priority.rb +21 -1
- data/test-unit/lib/test/unit/testcase.rb +101 -36
- data/test-unit/lib/test/unit/ui/console/testrunner.rb +7 -4
- data/test-unit/test/{test_testcase.rb → test-testcase.rb} +30 -1
- data/test-unit/test/test_assertions.rb +1 -1
- data/test/active-groonga-test-utils.rb +25 -26
- data/test/test-base.rb +16 -6
- data/test/test-schema-dumper.rb +48 -0
- data/test/test-schema.rb +20 -4
- data/test/tmp/database/database.groonga +0 -0
- data/test/tmp/database/database.groonga.0000000 +0 -0
- data/test/tmp/database/database.groonga.0000100 +0 -0
- data/test/tmp/database/database.groonga.0000101 +0 -0
- metadata +19 -7
data/NEWS.ja.rdoc
CHANGED
data/NEWS.rdoc
CHANGED
data/README.ja.rdoc
CHANGED
data/README.rdoc
CHANGED
data/Rakefile
CHANGED
@@ -27,8 +27,6 @@ require 'hoe'
|
|
27
27
|
|
28
28
|
ENV["NODOT"] = "yes"
|
29
29
|
|
30
|
-
Hoe::Test::SUPPORTED_TEST_FRAMEWORKS[:testunit2] = "test/run-test.rb"
|
31
|
-
|
32
30
|
base_dir = File.join(File.dirname(__FILE__))
|
33
31
|
truncate_base_dir = Proc.new do |x|
|
34
32
|
x.gsub(/^#{Regexp.escape(base_dir + File::SEPARATOR)}/, '')
|
@@ -87,6 +85,7 @@ ENV["VERSION"] ||= guess_version
|
|
87
85
|
version = ENV["VERSION"]
|
88
86
|
project = nil
|
89
87
|
Hoe.spec('activegroonga') do |_project|
|
88
|
+
Hoe::Test::SUPPORTED_TEST_FRAMEWORKS[:testunit2] = "test/run-test.rb"
|
90
89
|
project = _project
|
91
90
|
project.version = version
|
92
91
|
project.rubyforge_name = 'groonga'
|
@@ -116,14 +115,14 @@ Hoe.spec('activegroonga') do |_project|
|
|
116
115
|
description = cleanup_white_space(entries[entries.index("Description") + 1])
|
117
116
|
project.summary, project.description, = description.split(/\n\n+/, 3)
|
118
117
|
|
119
|
-
project.need_tar = false
|
120
118
|
project.remote_rdoc_dir = "active_groonga"
|
121
119
|
end
|
122
120
|
|
123
121
|
project.spec.dependencies.delete_if {|dependency| dependency.name == "hoe"}
|
124
122
|
|
125
123
|
ObjectSpace.each_object(Rake::RDocTask) do |rdoc_task|
|
126
|
-
|
124
|
+
options = rdoc_task.options
|
125
|
+
t_option_index = options.index("-t") || options.index("--title")
|
127
126
|
rdoc_task.options[t_option_index, 2] = nil
|
128
127
|
rdoc_task.title = "ActiveGroonga - #{version}"
|
129
128
|
rdoc_task.rdoc_files = Dir.glob("lib/**/*.rb")
|
data/lib/active_groonga.rb
CHANGED
@@ -46,6 +46,8 @@ module ActiveGroonga
|
|
46
46
|
autoload :Callbacks, 'active_groonga/callbacks'
|
47
47
|
autoload :Column, 'active_groonga/column'
|
48
48
|
autoload :Dirty, 'active_groonga/dirty'
|
49
|
+
autoload :DynamicRecordExpressionBuilder,
|
50
|
+
'active_groonga/dynamic_record_expression_builder'
|
49
51
|
autoload :Migration, 'active_groonga/migration'
|
50
52
|
autoload :Migrator, 'active_groonga/migration'
|
51
53
|
autoload :NamedScope, 'active_groonga/named_scope'
|
data/lib/active_groonga/base.rb
CHANGED
@@ -480,8 +480,9 @@ module ActiveGroonga
|
|
480
480
|
defined?(@abstract_class) && @abstract_class == true
|
481
481
|
end
|
482
482
|
|
483
|
-
def find(*args)
|
483
|
+
def find(*args, &block)
|
484
484
|
options = args.extract_options!
|
485
|
+
options = options.merge(:expression => block) if block
|
485
486
|
validate_find_options(options)
|
486
487
|
set_readonly_option!(options)
|
487
488
|
|
@@ -528,11 +529,11 @@ module ActiveGroonga
|
|
528
529
|
end
|
529
530
|
|
530
531
|
def groonga_table_name(name=nil)
|
531
|
-
|
532
|
+
(name || table_name).to_s
|
532
533
|
end
|
533
534
|
|
534
535
|
def groonga_metadata_table_name(name)
|
535
|
-
"
|
536
|
+
"meta-#{name}"
|
536
537
|
end
|
537
538
|
|
538
539
|
# Defines an "attribute" method (like +inheritance_column+ or
|
@@ -588,11 +589,12 @@ module ActiveGroonga
|
|
588
589
|
unless File.exist?(database_directory)
|
589
590
|
FileUtils.mkdir_p(database_directory)
|
590
591
|
end
|
592
|
+
database_directory = File.expand_path(database_directory)
|
591
593
|
database_file = File.join(database_directory, "database.groonga")
|
592
594
|
if File.exist?(database_file)
|
593
|
-
|
595
|
+
Groonga::Database.new(database_file)
|
594
596
|
else
|
595
|
-
|
597
|
+
Groonga::Database.create(:path => database_file)
|
596
598
|
end
|
597
599
|
self.database_directory = database_directory
|
598
600
|
end
|
@@ -610,14 +612,26 @@ module ActiveGroonga
|
|
610
612
|
directory
|
611
613
|
end
|
612
614
|
|
615
|
+
def index_columns_directory(table_name, target_table_name)
|
616
|
+
directory = File.join(columns_directory(table_name), target_table_name)
|
617
|
+
FileUtils.mkdir_p(directory) unless File.exist?(directory)
|
618
|
+
directory
|
619
|
+
end
|
620
|
+
|
613
621
|
def metadata_directory
|
614
622
|
directory = File.join(database_directory, "metadata")
|
615
623
|
FileUtils.mkdir_p(directory) unless File.exist?(directory)
|
616
624
|
directory
|
617
625
|
end
|
618
626
|
|
619
|
-
def count
|
620
|
-
|
627
|
+
def count(expression=nil)
|
628
|
+
if expression
|
629
|
+
table.select do |record|
|
630
|
+
expression.call(DynamicRecordExpressionBuilder.new(record))
|
631
|
+
end.size
|
632
|
+
else
|
633
|
+
table.size
|
634
|
+
end
|
621
635
|
end
|
622
636
|
|
623
637
|
private
|
@@ -627,56 +641,39 @@ module ActiveGroonga
|
|
627
641
|
end
|
628
642
|
|
629
643
|
def find_every(options)
|
630
|
-
|
631
|
-
conditions = (options[:conditions] || {}).stringify_keys
|
644
|
+
expression = options[:expression]
|
632
645
|
include_associations = merge_includes(scope(:find, :include), options[:include])
|
633
646
|
|
634
647
|
if include_associations.any? && references_eager_loaded_tables?(options)
|
635
648
|
records = find_with_associations(options)
|
636
649
|
else
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
index_records = nil
|
641
|
-
Schema.indexes(table_name).each do |index_definition|
|
642
|
-
if conditions.has_key?(index_definition.column)
|
643
|
-
index_column_name =
|
644
|
-
"#{index_definition.table}/#{index_definition.column}"
|
645
|
-
index = Schema.index_table.column(index_column_name)
|
646
|
-
key = conditions.delete(index_definition.column)
|
647
|
-
index_records = index.search(key, :result => index_records)
|
648
|
-
end
|
649
|
-
end
|
650
|
-
if index_records
|
651
|
-
sorted_records = index_records.sort([
|
652
|
-
:key => ".:score",
|
653
|
-
:order => :descending,
|
654
|
-
],
|
655
|
-
:limit => limit)
|
656
|
-
limit = sorted_records.size
|
657
|
-
target_records = sorted_records.records(:order => :ascending).collect do |record|
|
658
|
-
index_record_id = record.value.unpack("i")[0]
|
659
|
-
index_record = Groonga::Record.new(index_records, index_record_id)
|
660
|
-
target_record = index_record.key
|
661
|
-
target_record.instance_variable_set("@score", index_record.score)
|
662
|
-
def target_record.score
|
663
|
-
@score
|
664
|
-
end
|
665
|
-
target_record
|
650
|
+
if expression
|
651
|
+
records = table.select do |record|
|
652
|
+
expression.call(DynamicRecordExpressionBuilder.new(record))
|
666
653
|
end
|
667
654
|
else
|
668
|
-
|
669
|
-
limit = target_records.size if limit.zero?
|
655
|
+
records = table.select
|
670
656
|
end
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
657
|
+
sort_options = {}
|
658
|
+
limit = options[:limit]
|
659
|
+
offset = options[:offset]
|
660
|
+
offset = Integer(offset) unless offset.nil?
|
661
|
+
if limit and offset.nil?
|
662
|
+
sort_options[:limit] = limit
|
663
|
+
end
|
664
|
+
records = records.sort([:key => ".:score", :order => :descending],
|
665
|
+
sort_options)
|
666
|
+
if offset
|
667
|
+
in_target = false
|
668
|
+
_records, records = records, []
|
669
|
+
_records.each_with_index do |record, i|
|
670
|
+
break if limit and limit <= records.size
|
671
|
+
in_target = i >= offset unless in_target
|
672
|
+
records << record if in_target
|
678
673
|
end
|
679
|
-
|
674
|
+
end
|
675
|
+
records = records.collect do |record|
|
676
|
+
instantiate(record, record.key.id, record.table.domain)
|
680
677
|
end
|
681
678
|
if include_associations.any?
|
682
679
|
preload_associations(records, include_associations)
|
@@ -751,7 +748,7 @@ module ActiveGroonga
|
|
751
748
|
end
|
752
749
|
end
|
753
750
|
|
754
|
-
VALID_FIND_OPTIONS = [:
|
751
|
+
VALID_FIND_OPTIONS = [:expression, :readonly, :limit, :offset]
|
755
752
|
def validate_find_options(options)
|
756
753
|
options.assert_valid_keys(VALID_FIND_OPTIONS)
|
757
754
|
end
|
@@ -778,41 +775,35 @@ module ActiveGroonga
|
|
778
775
|
# Finder methods must instantiate through this method to work with the
|
779
776
|
# single-table inheritance model that makes it possible to create
|
780
777
|
# objects of different types from the same table.
|
781
|
-
def instantiate(record)
|
782
|
-
|
783
|
-
|
784
|
-
# No type given.
|
785
|
-
if subclass_name.empty?
|
786
|
-
allocate
|
778
|
+
def instantiate(record, id=nil, table=nil)
|
779
|
+
id ||= record.id
|
780
|
+
table ||= record.table
|
787
781
|
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
end
|
805
|
-
end
|
806
|
-
else
|
807
|
-
allocate
|
782
|
+
subclass_name = nil
|
783
|
+
if record.have_column?(inheritance_column)
|
784
|
+
subclass_name = record[inheritance_column]
|
785
|
+
end
|
786
|
+
|
787
|
+
if subclass_name.blank? or !columns_hash.include?(inheritance_column)
|
788
|
+
object = allocate
|
789
|
+
else
|
790
|
+
begin
|
791
|
+
object = compute_type(subclass_name).allocate
|
792
|
+
rescue NameError
|
793
|
+
raise SubclassNotFound,
|
794
|
+
"The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. " +
|
795
|
+
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
|
796
|
+
"Please rename this column if you didn't intend it to be used for storing the inheritance class " +
|
797
|
+
"or overwrite #{self.to_s}.inheritance_column to use another column for that information."
|
808
798
|
end
|
799
|
+
end
|
809
800
|
|
810
|
-
object.instance_variable_set("@id",
|
801
|
+
object.instance_variable_set("@id", id)
|
811
802
|
object.instance_variable_set("@score", record.score)
|
812
803
|
attributes = {}
|
813
|
-
|
814
|
-
|
815
|
-
attributes[column_name] =
|
804
|
+
table.columns.each do |column|
|
805
|
+
column_name = column.local_name
|
806
|
+
attributes[column_name] = record[".#{column_name}"]
|
816
807
|
end
|
817
808
|
object.instance_variable_set("@attributes", attributes)
|
818
809
|
object.instance_variable_set("@attributes_cache", Hash.new)
|
@@ -829,15 +820,18 @@ module ActiveGroonga
|
|
829
820
|
end
|
830
821
|
|
831
822
|
# Enables dynamic finders like <tt>find_by_user_name(user_name)</tt> and <tt>find_by_user_name_and_password(user_name, password)</tt>
|
832
|
-
# that are turned into <tt>find(:first
|
833
|
-
# <tt>find(:first
|
834
|
-
#
|
823
|
+
# that are turned into <tt>find(:first) {|record| record["user_name"] == user_name}</tt> and
|
824
|
+
# <tt>find(:first) {|record| (record["user_name"] ==
|
825
|
+
# user_name) & (record["password"] == password)}</tt> respectively. Also works for
|
826
|
+
# <tt>find(:all)</tt> by using
|
827
|
+
# <tt>find_all_by_amount(50)</tt> that is turned into
|
828
|
+
# <tt>find(:all) {|record| record["amount"] == 50}</tt>.
|
835
829
|
#
|
836
830
|
# It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+
|
837
831
|
# is actually <tt>find_all_by_amount(amount, options)</tt>.
|
838
832
|
#
|
839
833
|
# Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that
|
840
|
-
# are turned into scoped(:
|
834
|
+
# are turned into scoped(:expression => Proc.new {|record| record["user_name"] == user_name}) and scoped(:expression => Proc.new {|record| (record["user_name"] == user_name) & (record["password"] == password)})
|
841
835
|
# respectively.
|
842
836
|
#
|
843
837
|
# Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future
|
@@ -851,15 +845,15 @@ module ActiveGroonga
|
|
851
845
|
bang = match.bang?
|
852
846
|
# def self.find_by_login_and_activated(*args)
|
853
847
|
# options = args.extract_options!
|
854
|
-
#
|
848
|
+
# expression = construct_expression_from_arguments(
|
855
849
|
# [:login,:activated],
|
856
850
|
# args
|
857
851
|
# )
|
858
|
-
# finder_options = { :
|
852
|
+
# finder_options = { :expression => expression }
|
859
853
|
# validate_find_options(options)
|
860
854
|
# set_readonly_option!(options)
|
861
855
|
#
|
862
|
-
# if options[:
|
856
|
+
# if options[:expression]
|
863
857
|
# with_scope(:find => finder_options) do
|
864
858
|
# find(:first, options)
|
865
859
|
# end
|
@@ -870,15 +864,15 @@ module ActiveGroonga
|
|
870
864
|
self.class_eval <<-EOC, __FILE__, __LINE__
|
871
865
|
def self.#{method_id}(*args)
|
872
866
|
options = args.extract_options!
|
873
|
-
|
867
|
+
expression = construct_expression_from_arguments(
|
874
868
|
[:#{attribute_names.join(',:')}],
|
875
869
|
args
|
876
870
|
)
|
877
|
-
finder_options = {
|
871
|
+
finder_options = {:expression => expression}
|
878
872
|
validate_find_options(options)
|
879
873
|
set_readonly_option!(options)
|
880
874
|
|
881
|
-
#{'result = ' if bang}if options[:
|
875
|
+
#{'result = ' if bang}if options[:expression]
|
882
876
|
with_scope(:find => finder_options) do
|
883
877
|
find(:#{finder}, options)
|
884
878
|
end
|
@@ -897,12 +891,13 @@ module ActiveGroonga
|
|
897
891
|
# if args[0].is_a?(Hash)
|
898
892
|
# guard_protected_attributes = true
|
899
893
|
# attributes = args[0].with_indifferent_access
|
900
|
-
#
|
894
|
+
# find_expression = attributes.slice(*[:user_id])
|
901
895
|
# else
|
902
|
-
#
|
896
|
+
# attributes = construct_attributes_from_arguments([:user_id], args)
|
897
|
+
# find_expression = construct_expression_from_arguments([:user_id], args)
|
903
898
|
# end
|
904
899
|
#
|
905
|
-
# options = { :
|
900
|
+
# options = { :expression => find_expression }
|
906
901
|
# set_readonly_option!(options)
|
907
902
|
#
|
908
903
|
# record = find(:first, options)
|
@@ -928,7 +923,8 @@ module ActiveGroonga
|
|
928
923
|
find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
|
929
924
|
end
|
930
925
|
|
931
|
-
|
926
|
+
find_expression = construct_expression_from_attributes(find_attributes)
|
927
|
+
options = { :expression => find_expression }
|
932
928
|
set_readonly_option!(options)
|
933
929
|
|
934
930
|
record = find(:first, options)
|
@@ -952,11 +948,11 @@ module ActiveGroonga
|
|
952
948
|
self.class_eval <<-EOC, __FILE__, __LINE__
|
953
949
|
def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
|
954
950
|
options = args.extract_options! # options = args.extract_options!
|
955
|
-
|
951
|
+
expression = construct_expression_from_arguments( # expression = construct_expression_from_arguments(
|
956
952
|
[:#{attribute_names.join(',:')}], args # [:user_name, :password], args
|
957
953
|
) # )
|
958
954
|
#
|
959
|
-
scoped(:
|
955
|
+
scoped(:expression => expression) # scoped(:expression => expression)
|
960
956
|
end # end
|
961
957
|
EOC
|
962
958
|
send(method_id, *arguments)
|
@@ -968,11 +964,43 @@ module ActiveGroonga
|
|
968
964
|
|
969
965
|
def construct_attributes_from_arguments(attribute_names, arguments)
|
970
966
|
attributes = {}
|
971
|
-
attribute_names.each_with_index
|
967
|
+
attribute_names.each_with_index do |name, i|
|
968
|
+
attributes[name] = arguments[i]
|
969
|
+
end
|
972
970
|
attributes
|
973
971
|
end
|
974
972
|
|
975
|
-
|
973
|
+
def construct_expression_from_attributes(attributes)
|
974
|
+
Proc.new do |record|
|
975
|
+
builder = nil
|
976
|
+
attributes.each do |name, value|
|
977
|
+
expression = (record[name] == value)
|
978
|
+
if builder
|
979
|
+
builder = builder & expression
|
980
|
+
else
|
981
|
+
builder = expression
|
982
|
+
end
|
983
|
+
end
|
984
|
+
builder
|
985
|
+
end
|
986
|
+
end
|
987
|
+
|
988
|
+
def construct_expression_from_arguments(attribute_names, arguments)
|
989
|
+
Proc.new do |record|
|
990
|
+
builder = nil
|
991
|
+
attribute_names.each_with_index do |name, i|
|
992
|
+
expression = (record[name] == arguments[i])
|
993
|
+
if builder
|
994
|
+
builder = builder & expression
|
995
|
+
else
|
996
|
+
builder = expression
|
997
|
+
end
|
998
|
+
end
|
999
|
+
builder
|
1000
|
+
end
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
# Similar in purpose to +expand_hash_expression_for_aggregates+.
|
976
1004
|
def expand_attribute_names_for_aggregates(attribute_names)
|
977
1005
|
expanded_attribute_names = []
|
978
1006
|
attribute_names.each do |attribute_name|
|
@@ -1381,7 +1409,6 @@ module ActiveGroonga
|
|
1381
1409
|
def update(attribute_names=@attributes.keys)
|
1382
1410
|
attribute_names = remove_readonly_attributes(attribute_names)
|
1383
1411
|
table = self.class.table
|
1384
|
-
indexes = Schema.indexes(table)
|
1385
1412
|
quoted_attributes = attributes_with_quotes(false, attribute_names)
|
1386
1413
|
quoted_attributes.each do |name, value|
|
1387
1414
|
column = table.column(name)
|
@@ -1395,7 +1422,6 @@ module ActiveGroonga
|
|
1395
1422
|
def create
|
1396
1423
|
table = self.class.table
|
1397
1424
|
record = table.add
|
1398
|
-
indexes = Schema.indexes(table)
|
1399
1425
|
quoted_attributes = attributes_with_quotes
|
1400
1426
|
quoted_attributes.each do |name, value|
|
1401
1427
|
record[name] = value
|