activegroonga 0.0.2 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|