extendi-cassandra_object 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.travis.yml +23 -0
- data/CHANGELOG +0 -0
- data/Gemfile +17 -0
- data/LICENSE +13 -0
- data/MIT-LICENSE +20 -0
- data/README.md +177 -0
- data/Rakefile +12 -0
- data/extendi-cassandra_object.gemspec +26 -0
- data/lib/cassandra_object.rb +73 -0
- data/lib/cassandra_object/adapters/abstract_adapter.rb +61 -0
- data/lib/cassandra_object/adapters/cassandra_adapter.rb +269 -0
- data/lib/cassandra_object/adapters/cassandra_schemaless_adapter.rb +306 -0
- data/lib/cassandra_object/attribute_methods.rb +96 -0
- data/lib/cassandra_object/attribute_methods/definition.rb +22 -0
- data/lib/cassandra_object/attribute_methods/dirty.rb +36 -0
- data/lib/cassandra_object/attribute_methods/primary_key.rb +25 -0
- data/lib/cassandra_object/attribute_methods/typecasting.rb +59 -0
- data/lib/cassandra_object/base.rb +33 -0
- data/lib/cassandra_object/base_schema.rb +11 -0
- data/lib/cassandra_object/base_schemaless.rb +11 -0
- data/lib/cassandra_object/base_schemaless_dynamic.rb +11 -0
- data/lib/cassandra_object/belongs_to.rb +63 -0
- data/lib/cassandra_object/belongs_to/association.rb +49 -0
- data/lib/cassandra_object/belongs_to/builder.rb +40 -0
- data/lib/cassandra_object/belongs_to/reflection.rb +30 -0
- data/lib/cassandra_object/callbacks.rb +29 -0
- data/lib/cassandra_object/core.rb +63 -0
- data/lib/cassandra_object/errors.rb +10 -0
- data/lib/cassandra_object/identity.rb +26 -0
- data/lib/cassandra_object/inspect.rb +25 -0
- data/lib/cassandra_object/log_subscriber.rb +44 -0
- data/lib/cassandra_object/model.rb +60 -0
- data/lib/cassandra_object/persistence.rb +203 -0
- data/lib/cassandra_object/railtie.rb +33 -0
- data/lib/cassandra_object/railties/controller_runtime.rb +45 -0
- data/lib/cassandra_object/schema.rb +83 -0
- data/lib/cassandra_object/schemaless.rb +83 -0
- data/lib/cassandra_object/scope.rb +86 -0
- data/lib/cassandra_object/scope/finder_methods.rb +54 -0
- data/lib/cassandra_object/scope/query_methods.rb +69 -0
- data/lib/cassandra_object/scoping.rb +27 -0
- data/lib/cassandra_object/serialization.rb +6 -0
- data/lib/cassandra_object/tasks/ks.rake +54 -0
- data/lib/cassandra_object/timestamps.rb +19 -0
- data/lib/cassandra_object/type.rb +16 -0
- data/lib/cassandra_object/types.rb +8 -0
- data/lib/cassandra_object/types/array_type.rb +16 -0
- data/lib/cassandra_object/types/base_type.rb +26 -0
- data/lib/cassandra_object/types/boolean_type.rb +20 -0
- data/lib/cassandra_object/types/date_type.rb +22 -0
- data/lib/cassandra_object/types/float_type.rb +16 -0
- data/lib/cassandra_object/types/integer_type.rb +20 -0
- data/lib/cassandra_object/types/json_type.rb +13 -0
- data/lib/cassandra_object/types/string_type.rb +19 -0
- data/lib/cassandra_object/types/time_type.rb +16 -0
- data/lib/cassandra_object/types/type_helper.rb +39 -0
- data/lib/cassandra_object/validations.rb +44 -0
- data/test/support/cassandra.rb +63 -0
- data/test/support/issue.rb +12 -0
- data/test/support/issue_dynamic.rb +12 -0
- data/test/support/issue_schema.rb +17 -0
- data/test/support/issue_schema_child.rb +17 -0
- data/test/support/issue_schema_father.rb +13 -0
- data/test/test_helper.rb +41 -0
- data/test/unit/active_model_test.rb +18 -0
- data/test/unit/adapters/adapter_test.rb +6 -0
- data/test/unit/attribute_methods/definition_test.rb +13 -0
- data/test/unit/attribute_methods/dirty_test.rb +72 -0
- data/test/unit/attribute_methods/primary_key_test.rb +26 -0
- data/test/unit/attribute_methods/typecasting_test.rb +119 -0
- data/test/unit/attribute_methods_test.rb +51 -0
- data/test/unit/base_test.rb +20 -0
- data/test/unit/belongs_to/reflection_test.rb +12 -0
- data/test/unit/belongs_to_test.rb +63 -0
- data/test/unit/callbacks_test.rb +46 -0
- data/test/unit/connection_test.rb +6 -0
- data/test/unit/connections/connections_test.rb +55 -0
- data/test/unit/core_test.rb +55 -0
- data/test/unit/identity_test.rb +26 -0
- data/test/unit/inspect_test.rb +26 -0
- data/test/unit/log_subscriber_test.rb +25 -0
- data/test/unit/persistence_schema_test.rb +156 -0
- data/test/unit/persistence_test.rb +266 -0
- data/test/unit/railties/controller_runtime_test.rb +48 -0
- data/test/unit/schema/tasks_test.rb +32 -0
- data/test/unit/schema_test.rb +115 -0
- data/test/unit/schemaless_test.rb +100 -0
- data/test/unit/scope/finder_methods_test.rb +117 -0
- data/test/unit/scope/query_methods_test.rb +32 -0
- data/test/unit/scoping_test.rb +7 -0
- data/test/unit/timestamps_test.rb +27 -0
- data/test/unit/types/array_type_test.rb +17 -0
- data/test/unit/types/base_type_test.rb +19 -0
- data/test/unit/types/boolean_type_test.rb +24 -0
- data/test/unit/types/date_type_test.rb +15 -0
- data/test/unit/types/float_type_test.rb +17 -0
- data/test/unit/types/integer_type_test.rb +19 -0
- data/test/unit/types/json_type_test.rb +23 -0
- data/test/unit/types/string_type_test.rb +25 -0
- data/test/unit/types/time_type_test.rb +14 -0
- data/test/unit/validations_test.rb +27 -0
- metadata +202 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
rake_tasks do
|
4
|
+
load 'cassandra_object/tasks/ks.rake'
|
5
|
+
end
|
6
|
+
|
7
|
+
initializer "cassandra_object.log_runtime" do |app|
|
8
|
+
ActiveSupport.on_load :cassandra_object do
|
9
|
+
pathname = Rails.root.join('config', 'cassandra.yml')
|
10
|
+
if pathname.exist?
|
11
|
+
config = YAML.load(pathname.read)
|
12
|
+
|
13
|
+
if config = config[Rails.env]
|
14
|
+
self.config = {
|
15
|
+
keyspace: config['keyspace'],
|
16
|
+
hosts: config['hosts'],
|
17
|
+
}
|
18
|
+
else
|
19
|
+
raise "Missing environment #{Rails.env} in cassandra.yml"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Expose database runtime to controller for logging.
|
26
|
+
initializer "cassandra_object.log_runtime" do |app|
|
27
|
+
require "cassandra_object/railties/controller_runtime"
|
28
|
+
ActiveSupport.on_load(:action_controller) do
|
29
|
+
include CassandraObject::Railties::ControllerRuntime
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'active_support/core_ext/module/attr_internal'
|
2
|
+
require 'cassandra_object/log_subscriber'
|
3
|
+
|
4
|
+
module CassandraObject
|
5
|
+
module Railties # :nodoc:
|
6
|
+
module ControllerRuntime #:nodoc:
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods # :nodoc:
|
10
|
+
def log_process_action(payload)
|
11
|
+
messages, cassandra_object_runtime = super, payload[:cassandra_object_runtime]
|
12
|
+
if cassandra_object_runtime.to_i > 0
|
13
|
+
messages << ("CassandraObject: %.1fms" % cassandra_object_runtime.to_f)
|
14
|
+
end
|
15
|
+
messages
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#private
|
20
|
+
|
21
|
+
attr_internal :cassandra_object_runtime
|
22
|
+
|
23
|
+
def process_action(action, *args)
|
24
|
+
# We also need to reset the runtime before each action
|
25
|
+
# because of queries in middleware or in cases we are streaming
|
26
|
+
# and it won't be cleaned up by the method below.
|
27
|
+
CassandraObject::LogSubscriber.reset_runtime
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def cleanup_view_runtime
|
32
|
+
runtime_before_render = CassandraObject::LogSubscriber.reset_runtime
|
33
|
+
runtime = super
|
34
|
+
runtime_after_render = CassandraObject::LogSubscriber.reset_runtime
|
35
|
+
self.cassandra_object_runtime = runtime_before_render + runtime_after_render
|
36
|
+
runtime - runtime_after_render
|
37
|
+
end
|
38
|
+
|
39
|
+
def append_info_to_payload(payload)
|
40
|
+
super
|
41
|
+
payload[:cassandra_object_runtime] = (cassandra_object_runtime || 0) + CassandraObject::LogSubscriber.reset_runtime
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'cassandra'
|
2
|
+
|
3
|
+
module CassandraObject
|
4
|
+
class Schema
|
5
|
+
|
6
|
+
class << self
|
7
|
+
DEFAULT_CREATE_KEYSPACE = {
|
8
|
+
'strategy_class' => 'SimpleStrategy',
|
9
|
+
'strategy_options' => 'replication_factor:1'
|
10
|
+
}
|
11
|
+
|
12
|
+
def create_keyspace(keyspace, options = nil)
|
13
|
+
stmt = "CREATE KEYSPACE #{keyspace} WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };"
|
14
|
+
|
15
|
+
options ||= {} #DEFAULT_CREATE_KEYSPACE
|
16
|
+
|
17
|
+
system_execute stmt #adapter.statement_with_options(stmt, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def drop_keyspace(keyspace, confirm = false)
|
21
|
+
if adapter.cassandra_version < 3
|
22
|
+
count = (system_execute "SELECT count(*) FROM schema_columnfamilies where keyspace_name = '#{keyspace}' ALLOW FILTERING;").rows.first['count']
|
23
|
+
else
|
24
|
+
count = (system_schema_execute "SELECT count(*) FROM tables where keyspace_name = '#{keyspace}';").rows.first['count']
|
25
|
+
end
|
26
|
+
if confirm || count == 0
|
27
|
+
system_execute "DROP KEYSPACE #{keyspace}"
|
28
|
+
else
|
29
|
+
raise "Cannot drop keyspace #{keyspace}. You must delete all tables before"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_column_family(column_family, options = {})
|
34
|
+
create_table column_family, options
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_table(table_name, options = {})
|
38
|
+
adapter.create_table table_name, options
|
39
|
+
end
|
40
|
+
|
41
|
+
def alter_column_family(column_family, instruction, options = {})
|
42
|
+
stmt = "ALTER TABLE #{column_family} #{instruction}"
|
43
|
+
keyspace_execute adapter.statement_with_options(stmt, options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def drop_column_family(column_family)
|
47
|
+
drop_table column_family
|
48
|
+
end
|
49
|
+
|
50
|
+
def drop_table(table_name, confirm = false)
|
51
|
+
adapter.drop_table table_name, confirm
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_index(column_family, column, index_name = nil)
|
55
|
+
stmt = "CREATE INDEX #{index_name.nil? ? '' : index_name} ON #{column_family} (#{column})"
|
56
|
+
keyspace_execute stmt
|
57
|
+
end
|
58
|
+
|
59
|
+
# If the index was not given a name during creation, the index name is <columnfamily_name>_<column_name>_idx.
|
60
|
+
def drop_index(index_name)
|
61
|
+
keyspace_execute "DROP INDEX #{index_name}"
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def adapter
|
67
|
+
@adapter ||= CassandraObject::Adapters::CassandraAdapter.new(CassandraObject::Base.config)
|
68
|
+
end
|
69
|
+
|
70
|
+
def keyspace_execute(cql)
|
71
|
+
adapter.schema_execute cql, CassandraObject::Base.config[:keyspace]
|
72
|
+
end
|
73
|
+
|
74
|
+
def system_schema_execute(cql)
|
75
|
+
adapter.schema_execute cql, 'system_schema'
|
76
|
+
end
|
77
|
+
|
78
|
+
def system_execute(cql)
|
79
|
+
adapter.schema_execute cql, 'system'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'cassandra'
|
2
|
+
|
3
|
+
module CassandraObject
|
4
|
+
class Schemaless
|
5
|
+
|
6
|
+
class << self
|
7
|
+
DEFAULT_CREATE_KEYSPACE = {
|
8
|
+
'strategy_class' => 'SimpleStrategy',
|
9
|
+
'strategy_options' => 'replication_factor:1'
|
10
|
+
}
|
11
|
+
|
12
|
+
def create_keyspace(keyspace, options = nil)
|
13
|
+
stmt = "CREATE KEYSPACE #{keyspace} WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };"
|
14
|
+
|
15
|
+
options ||= {} #DEFAULT_CREATE_KEYSPACE
|
16
|
+
|
17
|
+
system_execute stmt #adapter.statement_with_options(stmt, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def drop_keyspace(keyspace, confirm = false)
|
21
|
+
if adapter.cassandra_version < 3
|
22
|
+
count = (system_execute "SELECT count(*) FROM schema_columnfamilies where keyspace_name = '#{keyspace}' ALLOW FILTERING;").rows.first['count']
|
23
|
+
else
|
24
|
+
count = (system_schema_execute "SELECT count(*) FROM tables where keyspace_name = '#{keyspace}';").rows.first['count']
|
25
|
+
end
|
26
|
+
if confirm || count == 0
|
27
|
+
system_execute "DROP KEYSPACE #{keyspace}"
|
28
|
+
else
|
29
|
+
raise "Cannot drop keyspace #{keyspace}. You must delete all tables before"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_column_family(column_family, options = {})
|
34
|
+
create_table column_family, options
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_table(table_name, options = {})
|
38
|
+
adapter.create_table table_name, options
|
39
|
+
end
|
40
|
+
|
41
|
+
def alter_column_family(column_family, instruction, options = {})
|
42
|
+
stmt = "ALTER TABLE #{column_family} #{instruction}"
|
43
|
+
keyspace_execute adapter.statement_with_options(stmt, options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def drop_column_family(column_family)
|
47
|
+
drop_table column_family
|
48
|
+
end
|
49
|
+
|
50
|
+
def drop_table(table_name, confirm = false)
|
51
|
+
adapter.drop_table table_name, confirm
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_index(column_family, column, index_name = nil)
|
55
|
+
stmt = "CREATE INDEX #{index_name.nil? ? '' : index_name} ON #{column_family} (#{column})"
|
56
|
+
keyspace_execute stmt
|
57
|
+
end
|
58
|
+
|
59
|
+
# If the index was not given a name during creation, the index name is <columnfamily_name>_<column_name>_idx.
|
60
|
+
def drop_index(index_name)
|
61
|
+
keyspace_execute "DROP INDEX #{index_name}"
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def adapter
|
67
|
+
@adapter ||= CassandraObject::Adapters::CassandraSchemalessAdapter.new(CassandraObject::Base.config)
|
68
|
+
end
|
69
|
+
|
70
|
+
def keyspace_execute(cql)
|
71
|
+
adapter.schema_execute cql, CassandraObject::Base.config[:keyspace]
|
72
|
+
end
|
73
|
+
|
74
|
+
def system_schema_execute(cql)
|
75
|
+
adapter.schema_execute cql, 'system_schema'
|
76
|
+
end
|
77
|
+
|
78
|
+
def system_execute(cql)
|
79
|
+
adapter.schema_execute cql, 'system'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'cassandra_object/scope/finder_methods'
|
2
|
+
require 'cassandra_object/scope/query_methods'
|
3
|
+
|
4
|
+
module CassandraObject
|
5
|
+
class Scope
|
6
|
+
include FinderMethods, QueryMethods
|
7
|
+
|
8
|
+
attr_accessor :klass
|
9
|
+
attr_accessor :is_all, :limit_value, :select_values, :where_values, :id_values, :raw_response, :next_cursor
|
10
|
+
|
11
|
+
def initialize(klass)
|
12
|
+
@klass = klass
|
13
|
+
|
14
|
+
@is_all = false
|
15
|
+
@limit_value = nil
|
16
|
+
@raw_response = nil
|
17
|
+
@select_values = []
|
18
|
+
@id_values = []
|
19
|
+
@where_values = []
|
20
|
+
@next_cursor = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def scoping
|
26
|
+
previous, klass.current_scope = klass.current_scope, self
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
klass.current_scope = previous
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def method_missing(method_name, *args, &block)
|
34
|
+
if klass.respond_to?(method_name)
|
35
|
+
scoping { klass.send(method_name, *args, &block) }
|
36
|
+
elsif Array.method_defined?(method_name)
|
37
|
+
to_a.send(method_name, *args, &block)
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def select_records
|
44
|
+
results = []
|
45
|
+
records = {}
|
46
|
+
new_next_cursor = nil
|
47
|
+
|
48
|
+
if self.schema_type == :standard
|
49
|
+
klass.adapter.select(self) do |key, attributes|
|
50
|
+
records[key] = attributes
|
51
|
+
end
|
52
|
+
else
|
53
|
+
if @is_all
|
54
|
+
pre = klass.adapter.pre_select(self, @limit_value, @next_cursor)
|
55
|
+
new_next_cursor = pre[:new_next_cursor]
|
56
|
+
return {results: [], next_cursor: new_next_cursor} if pre[:ids].empty? # fix last query all if ids is empty
|
57
|
+
self.id_values = pre[:ids]
|
58
|
+
end
|
59
|
+
|
60
|
+
resp = klass.adapter.select(self)
|
61
|
+
primary_key_column = klass.adapter.primary_key_column
|
62
|
+
resp.each do |cql_row|
|
63
|
+
key = cql_row[primary_key_column]
|
64
|
+
records[key] ||= {}
|
65
|
+
records[key][cql_row.values[1]] = cql_row.values[2]
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
# limit
|
70
|
+
records = records.first(@limit_value) if @limit_value.present?
|
71
|
+
records.each do |key, attributes|
|
72
|
+
if self.raw_response || self.schema_type == :dynamic_attributes
|
73
|
+
results << {key => attributes.values.compact.empty? ? attributes.keys : attributes}
|
74
|
+
else
|
75
|
+
results << klass.instantiate(key, attributes)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
results = results.reduce({}, :merge) if self.schema_type == :dynamic_attributes
|
79
|
+
if @is_all
|
80
|
+
return {results: results, next_cursor: new_next_cursor}
|
81
|
+
end
|
82
|
+
return results
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
class Scope
|
3
|
+
module FinderMethods
|
4
|
+
def find(ids)
|
5
|
+
if ids.is_a?(Array)
|
6
|
+
find_some(ids)
|
7
|
+
else
|
8
|
+
find_one(ids)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_by_id(ids)
|
13
|
+
find(ids)
|
14
|
+
rescue CassandraObject::RecordNotFound
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_all_in_batches(next_cursor = nil)
|
19
|
+
obj = self.clone
|
20
|
+
obj.is_all = true
|
21
|
+
obj.next_cursor = next_cursor
|
22
|
+
obj.to_a
|
23
|
+
end
|
24
|
+
|
25
|
+
def first
|
26
|
+
return limit(1).find_all_in_batches[:results].first if self.schema_type == :dynamic_attributes || self.schema_type == :schemaless
|
27
|
+
limit(1).to_a.first
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def find_one(id)
|
33
|
+
if id.blank?
|
34
|
+
raise CassandraObject::RecordNotFound, "Couldn't find #{self.name} with key #{id.inspect}"
|
35
|
+
elsif self.schema_type == :dynamic_attributes
|
36
|
+
record = where_ids(id).to_a
|
37
|
+
raise CassandraObject::RecordNotFound if record.empty?
|
38
|
+
record
|
39
|
+
elsif record = where_ids(id)[0]
|
40
|
+
record
|
41
|
+
else
|
42
|
+
raise CassandraObject::RecordNotFound
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_some(ids)
|
47
|
+
ids = ids.flatten
|
48
|
+
return [] if ids.empty?
|
49
|
+
ids = ids.compact.map(&:to_s).uniq
|
50
|
+
where_ids(ids).to_a
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
class Scope
|
3
|
+
module QueryMethods
|
4
|
+
|
5
|
+
def cql_response
|
6
|
+
cloned = self.clone
|
7
|
+
cloned.raw_response = true
|
8
|
+
cloned
|
9
|
+
end
|
10
|
+
|
11
|
+
def columns
|
12
|
+
cloned = self.clone
|
13
|
+
cloned.select_values = [:column1]
|
14
|
+
cloned.cql_response
|
15
|
+
end
|
16
|
+
|
17
|
+
def select!(*values)
|
18
|
+
self.select_values += values.flatten
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def select(*values, &block)
|
23
|
+
if block_given?
|
24
|
+
to_a.select(&block)
|
25
|
+
else
|
26
|
+
clone.select!(*values)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def where!(*values)
|
31
|
+
if values.flatten.size == 1
|
32
|
+
self.where_values += values.flatten
|
33
|
+
self.where_values << ''
|
34
|
+
else
|
35
|
+
self.where_values += values.flatten
|
36
|
+
end
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def where(*values)
|
41
|
+
clone.where! values
|
42
|
+
end
|
43
|
+
|
44
|
+
def where_ids!(*ids)
|
45
|
+
self.id_values += ids.flatten
|
46
|
+
self.id_values.compact if self.id_values.present?
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def where_ids(*ids)
|
51
|
+
clone.where_ids! ids
|
52
|
+
end
|
53
|
+
|
54
|
+
def limit!(value)
|
55
|
+
self.limit_value = value
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def limit(value)
|
60
|
+
clone.limit! value
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_a
|
64
|
+
select_records
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|