cequel 1.0.0.rc1 → 1.0.0.rc2

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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/lib/cequel.rb +18 -0
  3. data/lib/cequel/errors.rb +8 -4
  4. data/lib/cequel/metal.rb +14 -0
  5. data/lib/cequel/metal/batch.rb +21 -11
  6. data/lib/cequel/metal/batch_manager.rb +74 -0
  7. data/lib/cequel/metal/cql_row_specification.rb +19 -6
  8. data/lib/cequel/metal/data_set.rb +400 -163
  9. data/lib/cequel/metal/deleter.rb +45 -11
  10. data/lib/cequel/metal/incrementer.rb +23 -10
  11. data/lib/cequel/metal/inserter.rb +19 -6
  12. data/lib/cequel/metal/keyspace.rb +82 -159
  13. data/lib/cequel/metal/logger.rb +71 -0
  14. data/lib/cequel/metal/logging.rb +47 -0
  15. data/lib/cequel/metal/new_relic_instrumentation.rb +26 -0
  16. data/lib/cequel/metal/row.rb +36 -10
  17. data/lib/cequel/metal/row_specification.rb +21 -8
  18. data/lib/cequel/metal/statement.rb +30 -6
  19. data/lib/cequel/metal/updater.rb +89 -12
  20. data/lib/cequel/metal/writer.rb +23 -14
  21. data/lib/cequel/record.rb +52 -6
  22. data/lib/cequel/record/association_collection.rb +13 -6
  23. data/lib/cequel/record/associations.rb +146 -54
  24. data/lib/cequel/record/belongs_to_association.rb +34 -7
  25. data/lib/cequel/record/bound.rb +69 -12
  26. data/lib/cequel/record/bulk_writes.rb +29 -1
  27. data/lib/cequel/record/callbacks.rb +22 -6
  28. data/lib/cequel/record/collection.rb +273 -36
  29. data/lib/cequel/record/configuration_generator.rb +5 -0
  30. data/lib/cequel/record/data_set_builder.rb +86 -0
  31. data/lib/cequel/record/dirty.rb +11 -8
  32. data/lib/cequel/record/errors.rb +38 -4
  33. data/lib/cequel/record/has_many_association.rb +42 -9
  34. data/lib/cequel/record/lazy_record_collection.rb +39 -10
  35. data/lib/cequel/record/mass_assignment.rb +14 -6
  36. data/lib/cequel/record/persistence.rb +157 -20
  37. data/lib/cequel/record/properties.rb +147 -24
  38. data/lib/cequel/record/railtie.rb +15 -2
  39. data/lib/cequel/record/record_set.rb +504 -75
  40. data/lib/cequel/record/schema.rb +77 -13
  41. data/lib/cequel/record/scoped.rb +16 -11
  42. data/lib/cequel/record/secondary_indexes.rb +42 -6
  43. data/lib/cequel/record/tasks.rb +2 -1
  44. data/lib/cequel/record/validations.rb +51 -11
  45. data/lib/cequel/schema.rb +9 -0
  46. data/lib/cequel/schema/column.rb +172 -33
  47. data/lib/cequel/schema/create_table_dsl.rb +62 -31
  48. data/lib/cequel/schema/keyspace.rb +106 -7
  49. data/lib/cequel/schema/migration_validator.rb +128 -0
  50. data/lib/cequel/schema/table.rb +183 -20
  51. data/lib/cequel/schema/table_property.rb +92 -34
  52. data/lib/cequel/schema/table_reader.rb +45 -15
  53. data/lib/cequel/schema/table_synchronizer.rb +101 -43
  54. data/lib/cequel/schema/table_updater.rb +114 -19
  55. data/lib/cequel/schema/table_writer.rb +31 -13
  56. data/lib/cequel/schema/update_table_dsl.rb +71 -40
  57. data/lib/cequel/type.rb +214 -53
  58. data/lib/cequel/util.rb +6 -9
  59. data/lib/cequel/version.rb +2 -1
  60. data/spec/examples/record/associations_spec.rb +12 -12
  61. data/spec/examples/record/persistence_spec.rb +5 -5
  62. data/spec/examples/record/record_set_spec.rb +62 -50
  63. data/spec/examples/schema/table_synchronizer_spec.rb +37 -11
  64. data/spec/examples/schema/table_updater_spec.rb +3 -3
  65. data/spec/examples/spec_helper.rb +2 -11
  66. data/spec/examples/type_spec.rb +3 -3
  67. metadata +23 -4
  68. data/lib/cequel/new_relic_instrumentation.rb +0 -22
@@ -1,69 +1,163 @@
1
1
  module Cequel
2
-
3
2
  module Schema
4
-
3
+ #
4
+ # Encapsulates a series of schema modification statements that can be
5
+ # applied to an existing table
6
+ #
5
7
  class TableUpdater
6
-
7
- def self.apply(keyspace, table_name)
8
- new(keyspace, table_name).
9
- tap { |updater| yield updater if block_given? }.
10
- apply
8
+ #
9
+ # Construct a table updater and apply the schema modifications to the
10
+ # given table.
11
+ #
12
+ # @param (see #initialize)
13
+ # @yieldparam updater [TableUpdater] instance of updater whose
14
+ # modifications will be applied to the named table
15
+ # @return [void]
16
+ #
17
+ def self.apply(keyspace, table_name, &block)
18
+ new(keyspace, table_name).tap(&block).apply
11
19
  end
12
20
 
21
+ #
22
+ # @param keyspace [Metal::Keyspace] keyspace containing the table
23
+ # @param table_name [Symbol] name of the table to modify
24
+ # @private
25
+ #
13
26
  def initialize(keyspace, table_name)
14
27
  @keyspace, @table_name = keyspace, table_name
15
28
  @statements = []
16
29
  end
17
30
  private_class_method :new
18
31
 
32
+ #
33
+ # Apply the schema modifications to the table schema in the database
34
+ #
35
+ # @return [void]
36
+ #
37
+ # @api private
38
+ #
19
39
  def apply
20
40
  statements.each { |statement| keyspace.execute(statement) }
21
41
  end
22
42
 
43
+ #
44
+ # Add a column to the table
45
+ #
46
+ # @param name [Symbol] the name of the column
47
+ # @param type [Symbol,Type] the type of the column
48
+ # @return [void]
49
+ #
23
50
  def add_column(name, type)
24
- add_data_column(Column.new(name, type))
51
+ add_data_column(Column.new(name, type(type)))
25
52
  end
26
53
 
54
+ #
55
+ # Add a list to the table
56
+ #
57
+ # @param name [Symbol] the name of the list
58
+ # @param type [Symbol,Type] the type of the list elements
59
+ # @return [void]
60
+ #
27
61
  def add_list(name, type)
28
- add_data_column(List.new(name, type))
62
+ add_data_column(List.new(name, type(type)))
29
63
  end
30
64
 
65
+ #
66
+ # Add a set to the table
67
+ #
68
+ # @param name [Symbol] the name of the set
69
+ # @param type [Symbol,Type] the type of the set elements
70
+ # @return [void]
71
+ #
31
72
  def add_set(name, type)
32
- add_data_column(Set.new(name, type))
73
+ add_data_column(Set.new(name, type(type)))
33
74
  end
34
75
 
76
+ #
77
+ # Add a map to the table
78
+ #
79
+ # @param name [Symbol] the name of the map
80
+ # @param key_type [Symbol,Type] the type of the map's keys
81
+ # @param value_type [Symbol,Type] the type of the map's values
82
+ # @return [void]
83
+ #
35
84
  def add_map(name, key_type, value_type)
36
- add_data_column(Map.new(name, key_type, value_type))
85
+ add_data_column(Map.new(name, type(key_type), type(value_type)))
37
86
  end
38
87
 
88
+ #
89
+ # Change an existing column's type
90
+ #
91
+ # @param name [Symbol] the name of the column
92
+ # @param type [Symbol,Type] the new type of the column
93
+ # @return [void]
94
+ #
95
+ # @note Changing the type of a CQL column does not modify the data
96
+ # currently stored in the column. Thus, client-side handling is needed
97
+ # to convert old values to the new type at read time. Cequel does not
98
+ # currently support this functionality, although it may in the future.
99
+ # Altering column types is not recommended.
100
+ #
39
101
  def change_column(name, type)
40
- alter_table("ALTER #{name} TYPE #{type.cql_name}")
102
+ alter_table("ALTER #{name} TYPE #{type(type).cql_name}")
41
103
  end
42
104
 
105
+ #
106
+ # Rename a column
107
+ #
108
+ # @param old_name [Symbol] the current name of the column
109
+ # @param new_name [Symbol] the new name of the column
110
+ # @return [void]
111
+ #
43
112
  def rename_column(old_name, new_name)
44
113
  alter_table(%(RENAME "#{old_name}" TO "#{new_name}"))
45
114
  end
46
115
 
116
+ #
117
+ # Change one or more table storage properties
118
+ #
119
+ # @param options [Hash] map of property names to new values
120
+ # @return [void]
121
+ #
122
+ # @see Table#add_property
123
+ #
47
124
  def change_properties(options)
48
- properties = options.
49
- map { |name, value| TableProperty.new(name, value).to_cql }
125
+ properties = options
126
+ .map { |name, value| TableProperty.build(name, value).to_cql }
50
127
  alter_table("WITH #{properties.join(' AND ')}")
51
128
  end
52
129
 
53
- def create_index(column, index_name)
54
- index_name ||= "#{table_name}_#{column}_idx"
55
- statements << "CREATE INDEX #{index_name} ON #{table_name} (#{column})"
130
+ #
131
+ # Create a secondary index
132
+ #
133
+ # @param column_name [Symbol] name of the column to add an index on
134
+ # @param index_name [Symbol] name of the index; will be inferred from
135
+ # convention if nil
136
+ # @return [void]
137
+ #
138
+ def create_index(column_name, index_name = nil)
139
+ index_name ||= "#{table_name}_#{column_name}_idx"
140
+ statements <<
141
+ "CREATE INDEX #{index_name} ON #{table_name} (#{column_name})"
56
142
  end
57
143
 
144
+ #
145
+ # Remove a secondary index
146
+ #
147
+ # @param index_name [Symbol] the name of the index to remove
148
+ # @return [void]
149
+ #
58
150
  def drop_index(index_name)
59
151
  statements << "DROP INDEX #{index_name}"
60
152
  end
61
153
 
154
+ # @!visibility protected
62
155
  def add_data_column(column)
63
156
  add_column_statement(column)
64
157
  end
65
158
 
66
159
  protected
160
+
67
161
  attr_reader :keyspace, :table_name, :statements
68
162
 
69
163
  private
@@ -76,8 +170,9 @@ module Cequel
76
170
  alter_table("ADD #{column.to_cql}")
77
171
  end
78
172
 
173
+ def type(type)
174
+ ::Cequel::Type[type]
175
+ end
79
176
  end
80
-
81
177
  end
82
-
83
178
  end
@@ -1,25 +1,44 @@
1
1
  module Cequel
2
-
3
2
  module Schema
4
-
5
-
3
+ #
4
+ # Creates a new table schema in the database
5
+ #
6
6
  class TableWriter
7
-
7
+ #
8
+ # Creates a new table schema in the database given an object
9
+ # representation of the schema to create
10
+ #
11
+ # @param (see #initialize)
12
+ # @return (see #apply)
13
+ #
8
14
  def self.apply(keyspace, table)
9
15
  new(keyspace, table).apply
10
16
  end
11
17
 
18
+ #
19
+ # @param keyspace [Keyspace] keyspace in which to create the table
20
+ # @param table [Table] object representation of table schema
21
+ # @private
22
+ #
12
23
  def initialize(keyspace, table)
13
24
  @keyspace, @table = keyspace, table
14
25
  end
15
26
  private_class_method :new
16
27
 
28
+ #
29
+ # Create the table in the keyspace
30
+ #
31
+ # @return [void]
32
+ #
33
+ # @api private
34
+ #
17
35
  def apply
18
36
  keyspace.execute(create_statement)
19
37
  index_statements.each { |statement| keyspace.execute(statement) }
20
38
  end
21
39
 
22
40
  protected
41
+
23
42
  attr_reader :keyspace, :table
24
43
 
25
44
  private
@@ -36,7 +55,8 @@ module Cequel
36
55
  table.data_columns.each do |column|
37
56
  if column.indexed?
38
57
  statements <<
39
- "CREATE INDEX #{column.index_name} ON #{table.name} (#{column.name})"
58
+ "CREATE INDEX #{column.index_name} " \
59
+ "ON #{table.name} (#{column.name})"
40
60
  end
41
61
  end
42
62
  end
@@ -51,8 +71,8 @@ module Cequel
51
71
  end
52
72
 
53
73
  def keys_cql
54
- partition_cql = table.partition_key_columns.
55
- map { |key| key.name }.join(', ')
74
+ partition_cql = table.partition_key_columns
75
+ .map { |key| key.name }.join(', ')
56
76
  if table.clustering_columns.any?
57
77
  nonpartition_cql =
58
78
  table.clustering_columns.map { |key| key.name }.join(', ')
@@ -63,19 +83,17 @@ module Cequel
63
83
  end
64
84
 
65
85
  def properties_cql
66
- properties_fragments = table.properties.
67
- map { |_, property| property.to_cql }
86
+ properties_fragments = table.properties
87
+ .map { |_, property| property.to_cql }
68
88
  properties_fragments << 'COMPACT STORAGE' if table.compact_storage?
69
89
  if table.clustering_columns.any?
70
90
  clustering_fragment =
71
91
  table.clustering_columns.map(&:clustering_order_cql).join(',')
72
- properties_fragments << "CLUSTERING ORDER BY (#{clustering_fragment})"
92
+ properties_fragments <<
93
+ "CLUSTERING ORDER BY (#{clustering_fragment})"
73
94
  end
74
95
  properties_fragments.join(' AND ') if properties_fragments.any?
75
96
  end
76
-
77
97
  end
78
-
79
98
  end
80
-
81
99
  end
@@ -1,60 +1,91 @@
1
1
  module Cequel
2
-
3
2
  module Schema
4
-
3
+ #
4
+ # DSL for describing a series of schema modification statements
5
+ #
5
6
  class UpdateTableDSL < BasicObject
6
-
7
+ extend ::Forwardable
8
+ #
9
+ # Describe a series of schema modifications and build a {TableUpdater}
10
+ # to encapsulate them
11
+ #
12
+ # @param (see #initialize)
13
+ # @yield a block evaluated in the context of an {UpdateTableDSL} instance
14
+ # @return [void]
15
+ #
16
+ # @api private
17
+ # @see Keyspace#update_table
18
+ #
7
19
  def self.apply(updater, &block)
8
20
  dsl = new(updater)
9
21
  dsl.instance_eval(&block)
10
22
  end
11
23
 
24
+ #
25
+ # @param updater [TableUpdater]
26
+ #
27
+ # @api private
28
+ #
12
29
  def initialize(updater)
13
30
  @updater = updater
14
31
  end
15
32
 
16
- def add_column(name, type)
17
- @updater.add_column(name, ::Cequel::Type[type])
18
- end
19
-
20
- def add_list(name, type)
21
- @updater.add_list(name, ::Cequel::Type[type])
22
- end
23
-
24
- def add_set(name, type)
25
- @updater.add_set(name, ::Cequel::Type[type])
26
- end
27
-
28
- def add_map(name, key_type, value_type)
29
- @updater.add_map(name, ::Cequel::Type[key_type],
30
- ::Cequel::Type[value_type])
31
- end
32
-
33
- def change_column(name, type)
34
- @updater.change_column(name, ::Cequel::Type[type])
35
- end
36
-
37
- def rename_column(old_name, new_name)
38
- @updater.rename_column(old_name, new_name)
39
- end
40
-
41
- def change_properties(options)
42
- @updater.change_properties(options)
43
- end
33
+ #
34
+ # @!method add_column(name, type)
35
+ # (see Cequel::Schema::TableUpdater#add_column)
36
+ #
37
+ def_delegator :@updater, :add_column
38
+
39
+ #
40
+ # @!method add_list(name, type)
41
+ # (see Cequel::Schema::TableUpdater#add_list)
42
+ #
43
+ def_delegator :@updater, :add_list
44
+
45
+ #
46
+ # @!method add_set(name, type)
47
+ # (see Cequel::Schema::TableUpdater#add_set)
48
+ #
49
+ def_delegator :@updater, :add_set
50
+
51
+ #
52
+ # @!method add_map(name, key_type, value_type)
53
+ # (see Cequel::Schema::TableUpdater#add_map)
54
+ #
55
+ def_delegator :@updater, :add_map
56
+
57
+ #
58
+ # @!method change_column(name, type)
59
+ # (see Cequel::Schema::TableUpdater#change_column)
60
+ #
61
+ def_delegator :@updater, :change_column
62
+
63
+ #
64
+ # @!method rename_column(old_name, new_name)
65
+ # (see Cequel::Schema::TableUpdater#rename_column)
66
+ #
67
+ def_delegator :@updater, :rename_column
68
+
69
+ #
70
+ # @!method change_properties(options)
71
+ # (see Cequel::Schema::TableUpdater#change_properties)
72
+ #
73
+ def_delegator :@updater, :change_properties
44
74
  alias_method :change_options, :change_properties
45
75
 
46
- def create_index(column_name, index_name = nil)
47
- @updater.create_index(column_name, index_name)
48
- end
76
+ #
77
+ # @!method create_index(column_name, index_name = nil)
78
+ # (see Cequel::Schema::TableUpdater#create_index
79
+ #
80
+ def_delegator :@updater, :create_index
49
81
  alias_method :add_index, :create_index
50
82
 
51
- def drop_index(index_name)
52
- @updater.drop_index(index_name)
53
- end
83
+ #
84
+ # @!method drop_index(index_name)
85
+ # (see Cequel::Schema::TableUpdater#drop_index)
86
+ #
87
+ def_delegator :@updater, :drop_index
54
88
  alias_method :remove_index, :drop_index
55
-
56
89
  end
57
-
58
90
  end
59
-
60
91
  end
data/lib/cequel/type.rb CHANGED
@@ -1,71 +1,172 @@
1
1
  require 'singleton'
2
2
 
3
3
  module Cequel
4
-
4
+ #
5
+ # The Type module encapsulates information about the CQL3 type system. Each
6
+ # type has a `cql_name`, which is the name of the type as defined in CQL, and
7
+ # an `internal_name`, which is the name of the type in the lower-level
8
+ # interface that is exposed when introspecting table information in the
9
+ # database.
10
+ #
11
+ # As well as knowing their respective names, types also know how to cast Ruby
12
+ # objects to the correct canonical class corresponding to the type. These
13
+ # implicit types are used by the underlying `cassandra-cql` library to
14
+ # determine how to represent values when passing them to Cassandra.
15
+ #
16
+ # @since 1.0.0
17
+ #
5
18
  module Type
6
-
19
+ # Raised if an unknown type is looked up
7
20
  UnknownType = Class.new(ArgumentError)
8
21
 
9
22
  BY_CQL_NAME = {}
10
23
  BY_INTERNAL_NAME = {}
11
24
 
25
+ #
26
+ # Register a type for lookup
27
+ #
28
+ # @param type [Type] a new type
29
+ # @return [void]
30
+ #
12
31
  def self.register(type)
13
32
  BY_CQL_NAME[type.cql_name] = type
14
33
  type.cql_aliases.each { |aliaz| BY_CQL_NAME[aliaz] = type }
15
- BY_INTERNAL_NAME[type.internal_name] = type
34
+ type.internal_names.each do |internal_name|
35
+ BY_INTERNAL_NAME[internal_name] = type
36
+ end
16
37
  end
17
38
 
39
+ #
40
+ # Return a type corresponding to the given input
41
+ #
42
+ # @param cql_name [Symbol,Base] CQL name of a type, or a type
43
+ # @return [Base] type with the given CQL name
44
+ #
18
45
  def self.[](cql_name)
19
46
  cql_name.is_a?(Base) ? cql_name : lookup_cql(cql_name)
20
47
  end
21
48
 
49
+ #
50
+ # Look up a type by CQL name
51
+ #
52
+ # @param cql_name [Symbol] CQL name of a type
53
+ # @return [Base] type with the given CQL name
54
+ # @raise [UnknownType] if no type by that name is registered
55
+ #
22
56
  def self.lookup_cql(cql_name)
23
57
  BY_CQL_NAME.fetch(cql_name.to_sym)
24
58
  rescue KeyError
25
59
  raise UnknownType, "Unrecognized CQL type #{cql_name.inspect}"
26
60
  end
27
61
 
62
+ #
63
+ # Look up a type by internal name
64
+ #
65
+ # @param internal_name [String] internal name of a type
66
+ # @return [Base] type with the given internal name
67
+ # @raise [UnknownType] if no type by that name is registered
68
+ #
28
69
  def self.lookup_internal(internal_name)
29
70
  BY_INTERNAL_NAME.fetch(internal_name)
30
71
  rescue KeyError
31
72
  raise UnknownType, "Unrecognized internal type #{internal_name.inspect}"
32
73
  end
33
74
 
75
+ #
76
+ # The base class for all type objects. Types are singletons.
77
+ #
78
+ # @abstract Subclasses should implement {#cast}, and may implement
79
+ # {#internal_names} if it cannot be inferred from the class name. The
80
+ # name of the type class should be the camel-cased CQL name of the type
81
+ #
34
82
  class Base
35
83
  include Singleton
36
84
 
85
+ #
86
+ # @return the name of the type used in CQL. This is also the name that is
87
+ # used in all of Cequel's public interfaces
88
+ #
37
89
  def cql_name
38
90
  self.class.name.demodulize.underscore.to_sym
39
91
  end
40
92
 
93
+ #
94
+ # @return [Array<Symbol>] other names used in CQL for this type
95
+ #
41
96
  def cql_aliases
42
97
  []
43
98
  end
44
99
 
100
+ #
101
+ # @return [Array<String>] full class name of this type used in
102
+ # Cassandra's underlying representation
103
+ #
104
+ # @deprecated use {internal_names}
105
+ #
45
106
  def internal_name
46
- "org.apache.cassandra.db.marshal.#{self.class.name.demodulize}Type"
107
+ internal_names.first
47
108
  end
48
109
 
110
+ #
111
+ # @return [Array<String>] full class name(s) of this type used in
112
+ # Cassandra's underlying representation (allows for multiple values for
113
+ # types that have different names between different versions)
114
+ #
115
+ def internal_names
116
+ ["org.apache.cassandra.db.marshal.#{self.class.name.demodulize}Type"]
117
+ end
118
+
119
+ #
120
+ # @param value the value to cast
121
+ # @return the value cast to the correct Ruby class for this type
122
+ #
49
123
  def cast(value)
50
124
  value
51
125
  end
52
126
 
127
+ #
128
+ # CQL only allows changing column types when the old type's binary
129
+ # representation is compatible with the new type.
130
+ #
131
+ # @return [Array<Type>] new types that columns of this type may be
132
+ # altered to
133
+ #
134
+ def compatible_types
135
+ [Type[:blob]]
136
+ end
137
+
138
+ #
139
+ # A string representation of this type
140
+ #
53
141
  def to_s
54
142
  cql_name.to_s
55
143
  end
56
-
57
144
  end
58
145
 
146
+ #
147
+ # Abstract superclass for types that represent character data
148
+ #
149
+ # @abstract Subclasses must implement `#encoding`, which returns the name
150
+ # of the Ruby encoding corresponding to the character encoding used for
151
+ # values of this type
152
+ #
59
153
  class String < Base
60
-
61
154
  def cast(value)
62
155
  str = String(value)
63
156
  str.encoding.name == encoding ? str : str.dup.force_encoding(encoding)
64
157
  end
65
-
66
158
  end
67
159
 
160
+ #
161
+ # `ascii` columns store 7-bit ASCII character data
162
+ #
163
+ # @see TK CQL3 documentation for ascii type
164
+ #
68
165
  class Ascii < String
166
+ def compatible_types
167
+ super + [Type[:text]]
168
+ end
169
+
69
170
  private
70
171
 
71
172
  def encoding
@@ -74,14 +175,19 @@ module Cequel
74
175
  end
75
176
  register Ascii.instance
76
177
 
178
+ #
179
+ # `blob` columns store arbitrary bytes of data, represented as 8-bit ASCII
180
+ # strings of hex digits
181
+ #
182
+ # @see TK CQL3 documentation for blob type
183
+ #
77
184
  class Blob < String
78
-
79
- def internal_name
80
- 'org.apache.cassandra.db.marshal.BytesType'
185
+ def internal_names
186
+ ['org.apache.cassandra.db.marshal.BytesType']
81
187
  end
82
188
 
83
189
  def cast(value)
84
- value = value.to_s(16) if Integer === value
190
+ value = value.to_s(16) if value.is_a?(Integer)
85
191
  super
86
192
  end
87
193
 
@@ -90,10 +196,14 @@ module Cequel
90
196
  def encoding
91
197
  'ASCII-8BIT'
92
198
  end
93
-
94
199
  end
95
200
  register Blob.instance
96
201
 
202
+ #
203
+ # `boolean` types store boolean values
204
+ #
205
+ # @see TK CQL3 documentation for boolean type
206
+ #
97
207
  class Boolean < Base
98
208
  def cast(value)
99
209
  !!value
@@ -101,26 +211,47 @@ module Cequel
101
211
  end
102
212
  register Boolean.instance
103
213
 
214
+ #
215
+ # Counter columns are a special type of column in Cassandra that can be
216
+ # incremented and decremented atomically. Counter columns cannot comingle
217
+ # with regular data columns in the same table. Unlike other columns,
218
+ # counter columns cannot be updated without Cassandra internally reading
219
+ # the existing state of the column
220
+ #
221
+ # @see TK CQL3 documentation for counter columns
222
+ #
104
223
  class Counter < Base
224
+ def internal_names
225
+ ['org.apache.cassandra.db.marshal.CounterColumnType']
226
+ end
105
227
 
106
- def internal_name
107
- 'org.apache.cassandra.db.marshal.CounterColumnType'
228
+ def compatible_types
229
+ []
108
230
  end
109
231
 
110
232
  def cast(value)
111
233
  Integer(value)
112
234
  end
113
-
114
235
  end
115
236
  register Counter.instance
116
237
 
238
+ #
239
+ # `decimal` columns store decimal numeric values
240
+ #
241
+ # @see TK CQL3 documentation for decimal columns
242
+ #
117
243
  class Decimal < Base
118
244
  def cast(value)
119
- BigDecimal === value ? value : BigDecimal.new(value, 0)
245
+ value.is_a?(BigDecimal) ? value : BigDecimal.new(value, 0)
120
246
  end
121
247
  end
122
248
  register Decimal.instance
123
249
 
250
+ #
251
+ # `double` columns store 64-bit floating-point numeric values
252
+ #
253
+ # @see TK CQL3 documentation for double columns
254
+ #
124
255
  class Double < Base
125
256
  def cast(value)
126
257
  Float(value)
@@ -128,44 +259,64 @@ module Cequel
128
259
  end
129
260
  register Double.instance
130
261
 
262
+ #
263
+ # TK
264
+ #
265
+ # @see TK CQL3 documentation for inet columns
266
+ #
131
267
  class Inet < Base
132
-
133
- def internal_name
134
- 'org.apache.cassandra.db.marshal.InetAddressType'
268
+ def internal_names
269
+ ['org.apache.cassandra.db.marshal.InetAddressType']
135
270
  end
136
-
137
271
  end
138
272
  register Inet.instance
139
273
 
274
+ #
275
+ # `int` columns store 32-bit integer values
276
+ #
277
+ # @see TK CQL3 documentation for int columns
278
+ #
140
279
  class Int < Base
141
-
142
- def internal_name
143
- 'org.apache.cassandra.db.marshal.Int32Type'
280
+ def internal_names
281
+ ['org.apache.cassandra.db.marshal.Int32Type']
144
282
  end
145
283
 
146
284
  def cast(value)
147
285
  Integer(value)
148
286
  end
149
-
150
287
  end
151
288
  register Int.instance
152
289
 
290
+ #
291
+ # `float` columns store 32-bit floating-point numeric values
292
+ #
293
+ # @see TK CQL3 documentation for float columns
294
+ #
153
295
  class Float < Double; end
154
296
  register Float.instance
155
297
 
156
- class Long < Int
157
-
158
- def internal_name
159
- 'org.apache.cassandra.db.marshal.LongType'
298
+ #
299
+ # `bigint` columns store 64-bit integer values
300
+ #
301
+ # @see TK CQL3 documentation for bigint columns
302
+ #
303
+ class Bigint < Int
304
+ def internal_names
305
+ ['org.apache.cassandra.db.marshal.LongType']
160
306
  end
161
-
162
307
  end
163
- register Long.instance
164
-
308
+ register Bigint.instance
309
+
310
+ #
311
+ # `text` columns store UTF-8 character data. They are also known as
312
+ # `varchar` columns; the names can be used interchangeably. Text columns do
313
+ # not have a length limit
314
+ #
315
+ # @see TK CQL3 documentation for text columns
316
+ #
165
317
  class Text < String
166
-
167
- def internal_name
168
- 'org.apache.cassandra.db.marshal.UTF8Type'
318
+ def internal_names
319
+ ['org.apache.cassandra.db.marshal.UTF8Type']
169
320
  end
170
321
 
171
322
  def cql_aliases
@@ -177,31 +328,38 @@ module Cequel
177
328
  def encoding
178
329
  'UTF-8'
179
330
  end
180
-
181
331
  end
182
332
  register Text.instance
183
333
 
334
+ #
335
+ # `timestamp` columns store timestamps. Timestamps do not include time zone
336
+ # data, and all input times are cast to UTC before being stored.
337
+ #
184
338
  class Timestamp < Base
185
-
186
- def internal_name
187
- 'org.apache.cassandra.db.marshal.DateType'
339
+ def internal_names
340
+ ['org.apache.cassandra.db.marshal.DateType',
341
+ 'org.apache.cassandra.db.marshal.TimestampType']
188
342
  end
189
343
 
190
344
  def cast(value)
191
- if ::String === value then Time.parse(value)
345
+ if value.is_a?(::String) then Time.parse(value)
192
346
  elsif value.respond_to?(:to_time) then value.to_time
193
- elsif Numeric === value then Time.at(value)
347
+ elsif value.is_a?(Numeric) then Time.at(value)
194
348
  else Time.parse(value.to_s)
195
349
  end.utc
196
350
  end
197
-
198
351
  end
199
352
  register Timestamp.instance
200
353
 
354
+ #
355
+ # `uuid` columns store type 1 and type 4 UUIDs. Cequel uses the
356
+ # `CassandraCQL::UUID` type to represent UUIDs in Ruby, since this is what
357
+ # the underlying `cassandra-cql` library expects. Other UUID formats are
358
+ # supported as inputs.
359
+ #
201
360
  class Uuid < Base
202
-
203
- def internal_name
204
- 'org.apache.cassandra.db.marshal.UUIDType'
361
+ def internal_names
362
+ ['org.apache.cassandra.db.marshal.UUIDType']
205
363
  end
206
364
 
207
365
  def cast(value)
@@ -211,28 +369,31 @@ module Cequel
211
369
  else CassandraCQL::UUID.new(value)
212
370
  end
213
371
  end
214
-
215
372
  end
216
373
  register Uuid.instance
217
374
 
375
+ #
376
+ # `timeuuid` columns are a special type of UUID column that support
377
+ # time-based queries. For instance, a `timeuuid` clustering column can be
378
+ # filtered by ranges of times into which the UUIDs must fall. This
379
+ # functionality presumes the use of type 1 UUIDs, which encode the
380
+ # timestamp of their creation.
381
+ #
218
382
  class Timeuuid < Uuid
219
-
220
- def internal_name
221
- 'org.apache.cassandra.db.marshal.TimeUUIDType'
383
+ def internal_names
384
+ ['org.apache.cassandra.db.marshal.TimeUUIDType']
222
385
  end
223
-
224
386
  end
225
387
  register Timeuuid.instance
226
388
 
389
+ #
390
+ # `varint` columns store arbitrary-length integer data
391
+ #
227
392
  class Varint < Int
228
-
229
393
  def internal_name
230
394
  'org.apache.cassandra.db.marshal.IntegerType'
231
395
  end
232
-
233
396
  end
234
397
  register Varint.instance
235
-
236
398
  end
237
-
238
399
  end