datastax_rails 1.2.3 → 2.0.3
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 +4 -4
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -8
- data/config/schema.xml.erb +22 -19
- data/config/solrconfig.xml.erb +1 -1
- data/lib/cql-rb_extensions.rb +27 -0
- data/lib/datastax_rails.rb +13 -17
- data/lib/datastax_rails/associations/association.rb +1 -4
- data/lib/datastax_rails/associations/collection_proxy.rb +0 -13
- data/lib/datastax_rails/attribute_assignment.rb +28 -91
- data/lib/datastax_rails/attribute_methods.rb +109 -44
- data/lib/datastax_rails/attribute_methods/before_type_cast.rb +71 -0
- data/lib/datastax_rails/attribute_methods/dirty.rb +52 -11
- data/lib/datastax_rails/attribute_methods/primary_key.rb +87 -0
- data/lib/datastax_rails/attribute_methods/read.rb +120 -0
- data/lib/datastax_rails/attribute_methods/typecasting.rb +52 -21
- data/lib/datastax_rails/attribute_methods/write.rb +59 -0
- data/lib/datastax_rails/base.rb +227 -236
- data/lib/datastax_rails/cassandra_only_model.rb +25 -19
- data/lib/datastax_rails/column.rb +384 -0
- data/lib/datastax_rails/connection.rb +12 -13
- data/lib/datastax_rails/cql/alter_column_family.rb +0 -1
- data/lib/datastax_rails/cql/base.rb +15 -3
- data/lib/datastax_rails/cql/column_family.rb +2 -2
- data/lib/datastax_rails/cql/create_column_family.rb +7 -18
- data/lib/datastax_rails/cql/delete.rb +4 -9
- data/lib/datastax_rails/cql/insert.rb +2 -8
- data/lib/datastax_rails/cql/select.rb +4 -4
- data/lib/datastax_rails/cql/update.rb +8 -17
- data/lib/datastax_rails/dynamic_model.rb +98 -0
- data/lib/datastax_rails/payload_model.rb +19 -31
- data/lib/datastax_rails/persistence.rb +39 -54
- data/lib/datastax_rails/railtie.rb +1 -0
- data/lib/datastax_rails/reflection.rb +1 -1
- data/lib/datastax_rails/relation.rb +20 -20
- data/lib/datastax_rails/relation/batches.rb +18 -16
- data/lib/datastax_rails/relation/facet_methods.rb +1 -1
- data/lib/datastax_rails/relation/finder_methods.rb +6 -10
- data/lib/datastax_rails/relation/search_methods.rb +62 -48
- data/lib/datastax_rails/rsolr_client_wrapper.rb +1 -1
- data/lib/datastax_rails/schema/cassandra.rb +34 -62
- data/lib/datastax_rails/schema/migrator.rb +9 -24
- data/lib/datastax_rails/schema/solr.rb +13 -30
- data/lib/datastax_rails/schema_cache.rb +67 -0
- data/lib/datastax_rails/timestamps.rb +84 -11
- data/lib/datastax_rails/types/dirty_collection.rb +88 -0
- data/lib/datastax_rails/types/dynamic_list.rb +14 -0
- data/lib/datastax_rails/types/dynamic_map.rb +32 -0
- data/lib/datastax_rails/types/dynamic_set.rb +10 -0
- data/lib/datastax_rails/util/solr_repair.rb +4 -5
- data/lib/datastax_rails/validations.rb +6 -12
- data/lib/datastax_rails/validations/uniqueness.rb +0 -4
- data/lib/datastax_rails/version.rb +1 -1
- data/lib/datastax_rails/wide_storage_model.rb +13 -29
- data/lib/schema_migration.rb +4 -0
- data/spec/datastax_rails/associations_spec.rb +0 -1
- data/spec/datastax_rails/attribute_methods_spec.rb +9 -6
- data/spec/datastax_rails/base_spec.rb +26 -0
- data/spec/datastax_rails/column_spec.rb +238 -0
- data/spec/datastax_rails/cql/select_spec.rb +1 -1
- data/spec/datastax_rails/cql/update_spec.rb +2 -2
- data/spec/datastax_rails/persistence_spec.rb +29 -15
- data/spec/datastax_rails/relation/batches_spec.rb +5 -5
- data/spec/datastax_rails/relation/finder_methods_spec.rb +0 -20
- data/spec/datastax_rails/relation/search_methods_spec.rb +8 -0
- data/spec/datastax_rails/relation_spec.rb +7 -0
- data/spec/datastax_rails/schema/migrator_spec.rb +5 -10
- data/spec/datastax_rails/schema/solr_spec.rb +1 -1
- data/spec/datastax_rails/types/dynamic_list_spec.rb +20 -0
- data/spec/datastax_rails/types/dynamic_map_spec.rb +22 -0
- data/spec/datastax_rails/types/dynamic_set_spec.rb +16 -0
- data/spec/dummy/config/application.rb +2 -1
- data/spec/dummy/config/datastax.yml +6 -3
- data/spec/dummy/config/environments/development.rb +4 -5
- data/spec/dummy/config/environments/test.rb +0 -5
- data/spec/dummy/log/development.log +18 -0
- data/spec/dummy/log/test.log +36 -0
- data/spec/feature/dynamic_fields_spec.rb +9 -0
- data/spec/feature/overloaded_tables_spec.rb +24 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/default_consistency_shared_examples.rb +2 -2
- data/spec/support/models.rb +28 -14
- metadata +212 -188
- data/lib/datastax_rails/identity.rb +0 -64
- data/lib/datastax_rails/identity/abstract_key_factory.rb +0 -29
- data/lib/datastax_rails/identity/custom_key_factory.rb +0 -37
- data/lib/datastax_rails/identity/hashed_natural_key_factory.rb +0 -10
- data/lib/datastax_rails/identity/natural_key_factory.rb +0 -39
- data/lib/datastax_rails/identity/uuid_key_factory.rb +0 -27
- data/lib/datastax_rails/type.rb +0 -16
- data/lib/datastax_rails/types.rb +0 -9
- data/lib/datastax_rails/types/array_type.rb +0 -86
- data/lib/datastax_rails/types/base_type.rb +0 -42
- data/lib/datastax_rails/types/binary_type.rb +0 -19
- data/lib/datastax_rails/types/boolean_type.rb +0 -22
- data/lib/datastax_rails/types/date_type.rb +0 -23
- data/lib/datastax_rails/types/float_type.rb +0 -18
- data/lib/datastax_rails/types/integer_type.rb +0 -18
- data/lib/datastax_rails/types/string_type.rb +0 -16
- data/lib/datastax_rails/types/text_type.rb +0 -15
- data/lib/datastax_rails/types/time_type.rb +0 -23
- data/spec/datastax_rails/types/float_type_spec.rb +0 -31
- data/spec/datastax_rails/types/integer_type_spec.rb +0 -31
- data/spec/datastax_rails/types/time_type_spec.rb +0 -28
@@ -0,0 +1,71 @@
|
|
1
|
+
module DatastaxRails
|
2
|
+
module AttributeMethods
|
3
|
+
# = Datastax Rails Attribute Methods Before Type Cast
|
4
|
+
#
|
5
|
+
# <tt>Datastax Rails::AttributeMethods::BeforeTypeCast</tt> provides a way to
|
6
|
+
# read the value of the attributes before typecasting and deserialization.
|
7
|
+
#
|
8
|
+
# class Task < DatastaxRails::Base
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# task = Task.new(id: '1', completed_on: '2012-10-21')
|
12
|
+
# task.id # => 1
|
13
|
+
# task.completed_on # => Sun, 21 Oct 2012
|
14
|
+
#
|
15
|
+
# task.attributes_before_type_cast
|
16
|
+
# # => {"id"=>"1", "completed_on"=>"2012-10-21", ... }
|
17
|
+
# task.read_attribute_before_type_cast('id') # => "1"
|
18
|
+
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
19
|
+
#
|
20
|
+
# In addition to #read_attribute_before_type_cast and #attributes_before_type_cast,
|
21
|
+
# it declares a method for all attributes with the <tt>*_before_type_cast</tt>
|
22
|
+
# suffix.
|
23
|
+
#
|
24
|
+
# task.id_before_type_cast # => "1"
|
25
|
+
# task.completed_on_before_type_cast # => "2012-10-21"
|
26
|
+
module BeforeTypeCast
|
27
|
+
extend ActiveSupport::Concern
|
28
|
+
|
29
|
+
included do
|
30
|
+
attribute_method_suffix "_before_type_cast"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the value of the attribute identified by +attr_name+ before
|
34
|
+
# typecasting and deserialization.
|
35
|
+
#
|
36
|
+
# class Task < DatastaxRails::Base
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# task = Task.new(id: '1', completed_on: '2012-10-21')
|
40
|
+
# task.read_attribute('id') # => 1
|
41
|
+
# task.read_attribute_before_type_cast('id') # => '1'
|
42
|
+
# task.read_attribute('completed_on') # => Sun, 21 Oct 2012
|
43
|
+
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
44
|
+
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
|
45
|
+
def read_attribute_before_type_cast(attr_name)
|
46
|
+
@attributes[attr_name.to_s]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a hash of attributes before typecasting and deserialization.
|
50
|
+
#
|
51
|
+
# class Task < DatastaxRails::Base
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# task = Task.new(title: nil, is_done: true, completed_on: '2012-10-21')
|
55
|
+
# task.attributes
|
56
|
+
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>Sun, 21 Oct 2012, "created_at"=>nil, "updated_at"=>nil}
|
57
|
+
# task.attributes_before_type_cast
|
58
|
+
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
|
59
|
+
def attributes_before_type_cast
|
60
|
+
@attributes
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Handle *_before_type_cast for method_missing.
|
66
|
+
def attribute_before_type_cast(attribute_name)
|
67
|
+
read_attribute_before_type_cast(attribute_name)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -3,6 +3,12 @@ module DatastaxRails
|
|
3
3
|
module Dirty
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
include ActiveModel::Dirty
|
6
|
+
|
7
|
+
included do
|
8
|
+
if self < ::DatastaxRails::Timestamps
|
9
|
+
raise "You cannot include Dirty after Timestamps"
|
10
|
+
end
|
11
|
+
end
|
6
12
|
|
7
13
|
# Attempts to +save+ the record and clears changed attributes if successful.
|
8
14
|
def save(*) #:nodoc:
|
@@ -22,23 +28,58 @@ module DatastaxRails
|
|
22
28
|
end
|
23
29
|
|
24
30
|
# <tt>reload</tt> the record and clears changed attributes.
|
25
|
-
def reload
|
31
|
+
def reload(*)
|
26
32
|
super.tap do
|
27
|
-
@previously_changed.
|
28
|
-
@changed_attributes.
|
33
|
+
@previously_changed.clear
|
34
|
+
@changed_attributes.clear
|
29
35
|
end
|
30
36
|
end
|
31
37
|
|
32
|
-
def write_attribute(
|
33
|
-
|
34
|
-
loaded_attributes[
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
changed_attributes
|
38
|
+
def write_attribute(attr, value)
|
39
|
+
attr = attr.to_s
|
40
|
+
loaded_attributes[attr] = true
|
41
|
+
|
42
|
+
# The attribute already has an unsaved change.
|
43
|
+
if attribute_changed?(attr)
|
44
|
+
old = @changed_attributes[attr]
|
45
|
+
@changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
|
46
|
+
else
|
47
|
+
old = clone_attribute_value(:read_attribute, attr)
|
48
|
+
@changed_attributes[attr] = old if _field_changed?(attr, old, value)
|
40
49
|
end
|
50
|
+
|
51
|
+
super
|
41
52
|
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def _field_changed?(attr, old, value)
|
56
|
+
if column = column_for_attribute(attr)
|
57
|
+
if column.number? && (changes_from_nil_to_empty_string?(column, old, value) ||
|
58
|
+
changes_from_zero_to_string?(old, value))
|
59
|
+
value = nil
|
60
|
+
else
|
61
|
+
value = column.type_cast(value, self)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
old != value
|
66
|
+
end
|
67
|
+
|
68
|
+
def changes_from_nil_to_empty_string?(column, old, value)
|
69
|
+
# We don't record it as a change if the value changes from nil to ''.
|
70
|
+
# If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
|
71
|
+
# be typecast back to 0 (''.to_i => 0)
|
72
|
+
(old.nil? || old == 0) && value.blank?
|
73
|
+
end
|
74
|
+
|
75
|
+
def changes_from_zero_to_string?(old, value)
|
76
|
+
# For columns with old 0 and value non-empty string
|
77
|
+
old == 0 && value.is_a?(String) && value.present? && non_zero?(value)
|
78
|
+
end
|
79
|
+
|
80
|
+
def non_zero?(value)
|
81
|
+
value !~ /\A0+(\.0+)?\z/
|
82
|
+
end
|
42
83
|
end
|
43
84
|
end
|
44
85
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module DatastaxRails
|
4
|
+
module AttributeMethods
|
5
|
+
module PrimaryKey
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# Returns this record's primary key value wrapped in an Array if one is
|
9
|
+
# available.
|
10
|
+
def to_key
|
11
|
+
key = self.id
|
12
|
+
[key] if key
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the primary key value.
|
16
|
+
def id
|
17
|
+
read_attribute(self.class.primary_key)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Sets the primary key value.
|
21
|
+
def id=(value)
|
22
|
+
write_attribute(self.class.primary_key, value) if self.class.primary_key
|
23
|
+
end
|
24
|
+
|
25
|
+
# Queries the primary key value.
|
26
|
+
def id?
|
27
|
+
query_attribute(self.class.primary_key)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the primary key value before type cast.
|
31
|
+
def id_before_type_cast
|
32
|
+
read_attribute_before_type_cast(self.class.primary_key)
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def attribute_method?(attr_name)
|
38
|
+
attr_name == 'id' || super
|
39
|
+
end
|
40
|
+
|
41
|
+
module ClassMethods
|
42
|
+
def define_method_attribute(attr_name)
|
43
|
+
super
|
44
|
+
|
45
|
+
if attr_name == primary_key && attr_name != 'id'
|
46
|
+
generated_attribute_methods.send(:alias_method, :id, primary_key)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast).to_set
|
51
|
+
|
52
|
+
# Defines the primary key field -- can be overridden in subclasses.
|
53
|
+
# Overwriting will negate any effect of the +primary_key_prefix_type+
|
54
|
+
# setting, though.
|
55
|
+
def primary_key
|
56
|
+
@primary_key || 'id'
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns a quoted version of the primary key name, used to construct
|
60
|
+
# CQL statements.
|
61
|
+
def quoted_primary_key
|
62
|
+
@quoted_primary_key ||= connection.quote_column_name(primary_key)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Sets the name of the primary key column.
|
66
|
+
#
|
67
|
+
# class Project < DatastaxRails::Base
|
68
|
+
# self.primary_key = 'sysid'
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# You can also define the +primary_key+ method yourself:
|
72
|
+
#
|
73
|
+
# class Project < DatastaxRails::Base
|
74
|
+
# def self.primary_key
|
75
|
+
# 'foo_' + super
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# Project.primary_key # => "foo_id"
|
80
|
+
def primary_key=(value)
|
81
|
+
@primary_key = value && value.to_s
|
82
|
+
@quoted_primary_key = nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module DatastaxRails
|
2
|
+
module AttributeMethods
|
3
|
+
module Read
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
|
7
|
+
|
8
|
+
included do
|
9
|
+
class_attribute :attribute_types_cached_by_default, instance_writer: false
|
10
|
+
self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# +cache_attributes+ allows you to declare which converted attribute
|
15
|
+
# values should be cached. Usually caching only pays off for attributes
|
16
|
+
# with expensive conversion methods, like time related columns (e.g.
|
17
|
+
# +created_at+, +updated_at+).
|
18
|
+
def cache_attributes(*attribute_names)
|
19
|
+
cached_attributes.merge attribute_names.map { |attr| attr.to_s }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the attributes which are cached. By default time related columns
|
23
|
+
# with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
|
24
|
+
def cached_attributes
|
25
|
+
@cached_attributes ||= columns.select { |c| cacheable_column?(c) }.map { |col| col.name }.to_set
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns +true+ if the provided attribute is being cached.
|
29
|
+
def cache_attribute?(attr_name)
|
30
|
+
cached_attributes.include?(attr_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
# We want to generate the methods via module_eval rather than
|
36
|
+
# define_method, because define_method is slower on dispatch.
|
37
|
+
# Evaluating many similar methods may use more memory as the instruction
|
38
|
+
# sequences are duplicated and cached (in MRI). define_method may
|
39
|
+
# be slower on dispatch, but if you're careful about the closure
|
40
|
+
# created, then define_method will consume much less memory.
|
41
|
+
#
|
42
|
+
# But sometimes the database might return columns with
|
43
|
+
# characters that are not allowed in normal method names (like
|
44
|
+
# 'my_column(omg)'. So to work around this we first define with
|
45
|
+
# the __temp__ identifier, and then use alias method to rename
|
46
|
+
# it to what we want.
|
47
|
+
#
|
48
|
+
# We are also defining a constant to hold the frozen string of
|
49
|
+
# the attribute name. Using a constant means that we do not have
|
50
|
+
# to allocate an object on each call to the attribute method.
|
51
|
+
# Making it frozen means that it doesn't get duped when used to
|
52
|
+
# key the @attributes_cache in read_attribute.
|
53
|
+
def define_method_attribute(name)
|
54
|
+
safe_name = name.unpack('h*').first
|
55
|
+
generated_attribute_methods::AttrNames.set_name_cache safe_name, name
|
56
|
+
|
57
|
+
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
58
|
+
def __temp__#{safe_name}
|
59
|
+
read_attribute(AttrNames::ATTR_#{safe_name})
|
60
|
+
end
|
61
|
+
alias_method #{name.inspect}, :__temp__#{safe_name}
|
62
|
+
undef_method :__temp__#{safe_name}
|
63
|
+
STR
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def cacheable_column?(column)
|
69
|
+
if attribute_types_cached_by_default == ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
|
70
|
+
! serialized_attributes.include? column.name
|
71
|
+
else
|
72
|
+
attribute_types_cached_by_default.include?(column.type)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns the value of the attribute identified by <tt>attr_name</tt> after
|
78
|
+
# it has been typecast (for example, "2004-12-12" in a data column is cast
|
79
|
+
# to a date object, like Date.new(2004, 12, 12)).
|
80
|
+
def read_attribute(attr_name)
|
81
|
+
name = attr_name.to_s
|
82
|
+
|
83
|
+
# If it's a lazily loaded attribute and hasn't been loaded yet, we need to do that now.
|
84
|
+
if(!loaded_attributes[name] && persisted? && !key.blank?)
|
85
|
+
@attributes[name.to_s] = self.class.select(name).with_cassandra.find(self.id).read_attribute(name)
|
86
|
+
loaded_attributes[name] = true
|
87
|
+
end
|
88
|
+
|
89
|
+
# If it's cached, just return it
|
90
|
+
# We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/jonleighton/3552829.
|
91
|
+
@attributes_cache[name] || @attributes_cache.fetch(name) {
|
92
|
+
column = @column_types_override[name] if @column_types_override
|
93
|
+
column ||= self.class.attribute_definitions[name]
|
94
|
+
|
95
|
+
return @attributes.fetch(name) {
|
96
|
+
if name == 'id' && self.class.primary_key != name
|
97
|
+
read_attribute(self.class.primary_key)
|
98
|
+
end
|
99
|
+
} unless column
|
100
|
+
|
101
|
+
value = @attributes.fetch(name) {
|
102
|
+
nil
|
103
|
+
}
|
104
|
+
|
105
|
+
if self.class.cache_attribute?(name)
|
106
|
+
@attributes_cache[name] = column.type_cast(value, self)
|
107
|
+
else
|
108
|
+
column.type_cast value, self
|
109
|
+
end
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def attribute(attribute_name)
|
116
|
+
read_attribute(attribute_name)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -10,14 +10,49 @@ module DatastaxRails
|
|
10
10
|
self.attribute_definitions = ActiveSupport::HashWithIndifferentAccess.new
|
11
11
|
self.lazy_attributes = []
|
12
12
|
self.readonly_attributes = []
|
13
|
-
|
13
|
+
end
|
14
|
+
|
15
|
+
# This is a hook for use by modules that need to do extra stuff to
|
16
|
+
# attributes when they are initialized. (e.g. attribute
|
17
|
+
# serialization)
|
18
|
+
def initialize_attributes(attributes) #:nodoc:
|
19
|
+
attrs = {}
|
20
|
+
Types::DirtyCollection.ignore_modifications do
|
21
|
+
attributes.each do |k,v|
|
22
|
+
if col = column_for_attribute(k)
|
23
|
+
if col.type == :map && k.to_s != col.name.to_s
|
24
|
+
# See if we have a matching dynamic attribute column
|
25
|
+
self.class.map_columns.each do |mcol|
|
26
|
+
if k.to_s.starts_with?(mcol.name.to_s)
|
27
|
+
attrs[mcol.name.to_s] ||= mcol.wrap_collection({}, self)
|
28
|
+
attrs[mcol.name.to_s][k.to_s] = v
|
29
|
+
end
|
30
|
+
end
|
31
|
+
else
|
32
|
+
attrs[k.to_s] = col.collection? ? col.wrap_collection(v, self) : v
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
attrs
|
14
38
|
end
|
15
39
|
|
16
40
|
module ClassMethods
|
17
41
|
# Provide some measure of compatibility with things that expect this from ActiveRecord.
|
18
42
|
def columns_hash
|
19
43
|
self.attribute_definitions
|
20
|
-
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Gives you all of the map columns (useful for detecting dynamic columns)
|
47
|
+
def map_columns
|
48
|
+
@map_columns ||= self.attribute_definitions.values.select {|c| c.type == :map}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a hash where the keys are column names and the values are
|
52
|
+
# default values when instantiating the DSR object for this table.
|
53
|
+
def column_defaults
|
54
|
+
@column_defaults ||= Hash[columns.map { |c| [c.name.to_s, c.default] }]
|
55
|
+
end
|
21
56
|
|
22
57
|
# We need to ensure that inherited classes get their own attribute definitions.
|
23
58
|
def inherited(child)
|
@@ -42,8 +77,8 @@ module DatastaxRails
|
|
42
77
|
#
|
43
78
|
# @param [Hash] options the options to use in setting up the attribute
|
44
79
|
def timestamps(options = {})
|
45
|
-
attribute(:created_at, options.update(:type => :
|
46
|
-
attribute(:updated_at, options.update(:type => :
|
80
|
+
attribute(:created_at, options.update(:type => :timestamp))
|
81
|
+
attribute(:updated_at, options.update(:type => :timestamp))
|
47
82
|
end
|
48
83
|
|
49
84
|
# @!method array(name, options = {})
|
@@ -52,6 +87,8 @@ module DatastaxRails
|
|
52
87
|
# @macro attr_doc
|
53
88
|
# @!method date(name, options = {})
|
54
89
|
# @macro attr_doc
|
90
|
+
# @!method datetime(name, options = {})
|
91
|
+
# @macro attr_doc
|
55
92
|
# @!method float(name, options = {})
|
56
93
|
# @macro attr_doc
|
57
94
|
# @!method integer(name, options = {})
|
@@ -64,11 +101,21 @@ module DatastaxRails
|
|
64
101
|
# @macro attr_doc
|
65
102
|
# @!method time(name, options = {})
|
66
103
|
# @macro attr_doc
|
104
|
+
# @!method timestamp(name, options = {})
|
105
|
+
# @macro attr_doc
|
67
106
|
# @!method time_with_zone(name, options = {})
|
68
107
|
# @macro attr_doc
|
108
|
+
# @!method uuid(name, options = {})
|
109
|
+
# @macro attr_doc
|
110
|
+
# @!method map(name, options = {})
|
111
|
+
# @macro attr_doc
|
112
|
+
# @!method list(name, options = {})
|
113
|
+
# @macro attr_doc
|
114
|
+
# @!method set(name, options = {})
|
115
|
+
# @macro attr_doc
|
69
116
|
|
70
117
|
# The following sets up a bunch of nearly identical attribute methods
|
71
|
-
%w(array boolean date float integer json string text time time_with_zone).each do |type|
|
118
|
+
%w(array boolean date datetime float integer json string text time timestamp time_with_zone uuid map set list).each do |type|
|
72
119
|
class_eval <<-EOV, __FILE__, __LINE__ + 1
|
73
120
|
def #{type}(name, options = {}) # def string(name, options = {})
|
74
121
|
attribute(name, options.update(:type => :#{type})) # attribute(name, options.update(type: :string))
|
@@ -77,22 +124,6 @@ module DatastaxRails
|
|
77
124
|
end
|
78
125
|
|
79
126
|
# @!endgroup
|
80
|
-
|
81
|
-
# Casts a single attribute according to the appropriate coder.
|
82
|
-
#
|
83
|
-
# @param [DatastaxRails::Base] record the record to which this attribute belongs
|
84
|
-
# @param [String] name the name of the attribute
|
85
|
-
# @param [Object] value the value of the attribute prior to typecasting
|
86
|
-
#
|
87
|
-
# @return the typecast value
|
88
|
-
# @raise [NoMethodError] if the attribute is unknown
|
89
|
-
def typecast_attribute(record, name, value)
|
90
|
-
if attribute_definition = attribute_definitions[name.to_sym]
|
91
|
-
attribute_definition.instantiate(record, value)
|
92
|
-
else
|
93
|
-
raise NoMethodError, "Unknown attribute #{name.inspect}"
|
94
|
-
end
|
95
|
-
end
|
96
127
|
end
|
97
128
|
end
|
98
129
|
end
|