cequel 0.5.6 → 1.0.0.pre.1

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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cequel.rb +5 -8
  3. data/lib/cequel/errors.rb +1 -0
  4. data/lib/cequel/metal.rb +17 -0
  5. data/lib/cequel/metal/batch.rb +62 -0
  6. data/lib/cequel/metal/cql_row_specification.rb +26 -0
  7. data/lib/cequel/metal/data_set.rb +461 -0
  8. data/lib/cequel/metal/deleter.rb +47 -0
  9. data/lib/cequel/metal/incrementer.rb +35 -0
  10. data/lib/cequel/metal/inserter.rb +53 -0
  11. data/lib/cequel/metal/keyspace.rb +213 -0
  12. data/lib/cequel/metal/row.rb +48 -0
  13. data/lib/cequel/metal/row_specification.rb +37 -0
  14. data/lib/cequel/metal/statement.rb +30 -0
  15. data/lib/cequel/metal/updater.rb +65 -0
  16. data/lib/cequel/metal/writer.rb +73 -0
  17. data/lib/cequel/model.rb +12 -84
  18. data/lib/cequel/model/association_collection.rb +23 -0
  19. data/lib/cequel/model/associations.rb +84 -80
  20. data/lib/cequel/model/base.rb +74 -0
  21. data/lib/cequel/model/belongs_to_association.rb +31 -0
  22. data/lib/cequel/model/callbacks.rb +14 -10
  23. data/lib/cequel/model/collection.rb +255 -0
  24. data/lib/cequel/model/errors.rb +6 -6
  25. data/lib/cequel/model/has_many_association.rb +26 -0
  26. data/lib/cequel/model/mass_assignment.rb +31 -0
  27. data/lib/cequel/model/persistence.rb +119 -115
  28. data/lib/cequel/model/properties.rb +89 -87
  29. data/lib/cequel/model/railtie.rb +21 -14
  30. data/lib/cequel/model/record_set.rb +285 -0
  31. data/lib/cequel/model/schema.rb +33 -0
  32. data/lib/cequel/model/scoped.rb +5 -48
  33. data/lib/cequel/model/validations.rb +18 -18
  34. data/lib/cequel/schema.rb +15 -0
  35. data/lib/cequel/schema/column.rb +135 -0
  36. data/lib/cequel/schema/create_table_dsl.rb +56 -0
  37. data/lib/cequel/schema/keyspace.rb +50 -0
  38. data/lib/cequel/schema/table.rb +120 -0
  39. data/lib/cequel/schema/table_property.rb +67 -0
  40. data/lib/cequel/schema/table_reader.rb +139 -0
  41. data/lib/cequel/schema/table_synchronizer.rb +114 -0
  42. data/lib/cequel/schema/table_updater.rb +83 -0
  43. data/lib/cequel/schema/table_writer.rb +80 -0
  44. data/lib/cequel/schema/update_table_dsl.rb +60 -0
  45. data/lib/cequel/type.rb +232 -0
  46. data/lib/cequel/version.rb +1 -1
  47. data/spec/environment.rb +5 -1
  48. data/spec/examples/metal/data_set_spec.rb +608 -0
  49. data/spec/examples/model/associations_spec.rb +84 -74
  50. data/spec/examples/model/callbacks_spec.rb +66 -59
  51. data/spec/examples/model/list_spec.rb +393 -0
  52. data/spec/examples/model/map_spec.rb +229 -0
  53. data/spec/examples/model/mass_assignment_spec.rb +55 -0
  54. data/spec/examples/model/naming_spec.rb +11 -4
  55. data/spec/examples/model/persistence_spec.rb +140 -150
  56. data/spec/examples/model/properties_spec.rb +122 -75
  57. data/spec/examples/model/record_set_spec.rb +285 -0
  58. data/spec/examples/model/schema_spec.rb +44 -0
  59. data/spec/examples/model/serialization_spec.rb +20 -14
  60. data/spec/examples/model/set_spec.rb +133 -0
  61. data/spec/examples/model/spec_helper.rb +0 -10
  62. data/spec/examples/model/validations_spec.rb +51 -38
  63. data/spec/examples/schema/table_reader_spec.rb +328 -0
  64. data/spec/examples/schema/table_synchronizer_spec.rb +172 -0
  65. data/spec/examples/schema/table_updater_spec.rb +157 -0
  66. data/spec/examples/schema/table_writer_spec.rb +225 -0
  67. data/spec/examples/spec_helper.rb +29 -0
  68. data/spec/examples/type_spec.rb +204 -0
  69. data/spec/support/helpers.rb +67 -8
  70. metadata +121 -152
  71. data/lib/cequel/batch.rb +0 -58
  72. data/lib/cequel/cql_row_specification.rb +0 -22
  73. data/lib/cequel/data_set.rb +0 -371
  74. data/lib/cequel/keyspace.rb +0 -205
  75. data/lib/cequel/model/class_internals.rb +0 -49
  76. data/lib/cequel/model/column.rb +0 -20
  77. data/lib/cequel/model/counter.rb +0 -35
  78. data/lib/cequel/model/dictionary.rb +0 -126
  79. data/lib/cequel/model/dirty.rb +0 -53
  80. data/lib/cequel/model/dynamic.rb +0 -31
  81. data/lib/cequel/model/inheritable.rb +0 -48
  82. data/lib/cequel/model/instance_internals.rb +0 -23
  83. data/lib/cequel/model/local_association.rb +0 -42
  84. data/lib/cequel/model/magic.rb +0 -79
  85. data/lib/cequel/model/mass_assignment_security.rb +0 -21
  86. data/lib/cequel/model/naming.rb +0 -17
  87. data/lib/cequel/model/observer.rb +0 -42
  88. data/lib/cequel/model/readable_dictionary.rb +0 -182
  89. data/lib/cequel/model/remote_association.rb +0 -40
  90. data/lib/cequel/model/scope.rb +0 -362
  91. data/lib/cequel/model/subclass_internals.rb +0 -45
  92. data/lib/cequel/model/timestamps.rb +0 -52
  93. data/lib/cequel/model/translation.rb +0 -17
  94. data/lib/cequel/row_specification.rb +0 -63
  95. data/lib/cequel/statement.rb +0 -23
  96. data/spec/examples/data_set_spec.rb +0 -444
  97. data/spec/examples/keyspace_spec.rb +0 -84
  98. data/spec/examples/model/counter_spec.rb +0 -94
  99. data/spec/examples/model/dictionary_spec.rb +0 -301
  100. data/spec/examples/model/dirty_spec.rb +0 -39
  101. data/spec/examples/model/dynamic_spec.rb +0 -41
  102. data/spec/examples/model/inheritable_spec.rb +0 -45
  103. data/spec/examples/model/magic_spec.rb +0 -199
  104. data/spec/examples/model/mass_assignment_security_spec.rb +0 -13
  105. data/spec/examples/model/observer_spec.rb +0 -86
  106. data/spec/examples/model/scope_spec.rb +0 -677
  107. data/spec/examples/model/timestamps_spec.rb +0 -52
  108. data/spec/examples/model/translation_spec.rb +0 -23
@@ -0,0 +1,139 @@
1
+ module Cequel
2
+
3
+ module Schema
4
+
5
+ class TableReader
6
+
7
+ COMPOSITE_TYPE_PATTERN =
8
+ /^org\.apache\.cassandra\.db\.marshal\.CompositeType\((.+)\)$/
9
+ REVERSED_TYPE_PATTERN =
10
+ /^org\.apache\.cassandra\.db\.marshal\.ReversedType\((.+)\)$/
11
+ COLLECTION_TYPE_PATTERN =
12
+ /^org\.apache\.cassandra\.db\.marshal\.(List|Set|Map)Type\((.+)\)$/
13
+
14
+ STORAGE_PROPERTIES = %w[bloom_filter_fp_chance caching comment compaction
15
+ compression dclocal_read_repair_chance gc_grace_seconds
16
+ read_repair_chance replicate_on_write]
17
+
18
+ attr_reader :table
19
+
20
+ def self.read(keyspace, table_name)
21
+ new(keyspace, table_name).read
22
+ end
23
+
24
+ def initialize(keyspace, table_name)
25
+ @keyspace, @table_name = keyspace, table_name
26
+ @table = Table.new(table_name.to_sym)
27
+ end
28
+ private_class_method(:new)
29
+
30
+ def read
31
+ if table_data.present?
32
+ read_partition_keys
33
+ read_clustering_columns
34
+ read_data_columns
35
+ read_properties
36
+ table
37
+ end
38
+ end
39
+
40
+ protected
41
+ attr_reader :keyspace, :table_name, :table
42
+
43
+ private
44
+
45
+ def read_partition_keys
46
+ validator = table_data['key_validator']
47
+ types = parse_composite_types(validator) || [validator]
48
+ JSON.parse(table_data['key_aliases']).zip(types) do |key_alias, type|
49
+ name = key_alias.to_sym
50
+ table.add_partition_key(key_alias.to_sym, Type.lookup_internal(type))
51
+ end
52
+ end
53
+
54
+ def read_clustering_columns
55
+ column_aliases = JSON.parse(table_data['column_aliases'])
56
+ comparators = parse_composite_types(table_data['comparator'])
57
+ unless comparators
58
+ table.compact_storage = true
59
+ comparators = [table_data['comparator']]
60
+ end
61
+ column_aliases.zip(comparators) do |column_alias, type|
62
+ if REVERSED_TYPE_PATTERN =~ type
63
+ type = $1
64
+ clustering_order = :desc
65
+ end
66
+ table.add_clustering_column(
67
+ column_alias.to_sym,
68
+ Type.lookup_internal(type),
69
+ clustering_order
70
+ )
71
+ end
72
+ end
73
+
74
+ def read_data_columns
75
+ column_data.each do |result|
76
+ if COLLECTION_TYPE_PATTERN =~ result['validator']
77
+ read_collection_column(
78
+ result['column_name'],
79
+ $1.underscore,
80
+ *$2.split(',')
81
+ )
82
+ else
83
+ table.add_data_column(
84
+ result['column_name'].to_sym,
85
+ Type.lookup_internal(result['validator']),
86
+ result['index_name'].try(:to_sym)
87
+ )
88
+ end
89
+ end
90
+ end
91
+
92
+ def read_collection_column(name, collection_type, *internal_types)
93
+ types = internal_types.map { |internal| Type.lookup_internal(internal) }
94
+ table.__send__("add_#{collection_type}", name.to_sym, *types)
95
+ end
96
+
97
+ def read_properties
98
+ table_data.slice(*STORAGE_PROPERTIES).each do |name, value|
99
+ table.add_property(name, value)
100
+ end
101
+ compaction = JSON.parse(table_data['compaction_strategy_options']).
102
+ symbolize_keys
103
+ compaction[:class] = table_data['compaction_strategy_class']
104
+ table.add_property(:compaction, compaction)
105
+ compression = JSON.parse(table_data['compression_parameters'])
106
+ table.add_property(:compression, compression)
107
+ end
108
+
109
+ def parse_composite_types(type_string)
110
+ if COMPOSITE_TYPE_PATTERN =~ type_string
111
+ $1.split(',')
112
+ end
113
+ end
114
+
115
+ def table_data
116
+ return @table_data if defined? @table_data
117
+ table_query = keyspace.execute(<<-CQL, keyspace.name, table_name)
118
+ SELECT * FROM system.schema_columnfamilies
119
+ WHERE keyspace_name = ? AND columnfamily_name = ?
120
+ CQL
121
+ @table_data = table_query.first.try(:to_hash)
122
+ end
123
+
124
+ def column_data
125
+ @column_data ||=
126
+ if table_data
127
+ column_query = keyspace.execute(<<-CQL, keyspace.name, table_name)
128
+ SELECT * FROM system.schema_columns
129
+ WHERE keyspace_name = ? AND columnfamily_name = ?
130
+ CQL
131
+ column_query.map(&:to_hash)
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+
139
+ end
@@ -0,0 +1,114 @@
1
+ module Cequel
2
+
3
+ module Schema
4
+
5
+ class TableSynchronizer
6
+
7
+ def self.apply(keyspace, existing, updated)
8
+ if existing
9
+ TableUpdater.apply(keyspace, existing.name) do |updater|
10
+ new(updater, existing, updated).apply
11
+ end
12
+ else
13
+ TableWriter.apply(keyspace, updated)
14
+ end
15
+ end
16
+
17
+ def initialize(updater, existing, updated)
18
+ @updater, @existing, @updated = updater, existing, updated
19
+ end
20
+ private_class_method :new
21
+
22
+ def apply
23
+ update_keys
24
+ update_columns
25
+ update_properties
26
+ end
27
+
28
+ protected
29
+ attr_reader :updater, :existing, :updated
30
+
31
+ private
32
+
33
+ def update_keys
34
+ each_key_pair do |old_key, new_key|
35
+ if old_key.type != new_key.type
36
+ raise InvalidSchemaMigration,
37
+ "Can't change type of key column #{old_key.name} from #{old_key.type} to #{new_key.type}"
38
+ end
39
+ if old_key.name != new_key.name
40
+ updater.rename_column(old_key.name, new_key.name)
41
+ end
42
+ end
43
+ end
44
+
45
+ def update_columns
46
+ each_column_pair do |old_column, new_column|
47
+ if old_column.nil?
48
+ add_column(new_column)
49
+ elsif new_column
50
+ if old_column.class != new_column.class
51
+ raise InvalidSchemaMigration,
52
+ "Can't change #{old_column.name} from #{old_column.class.name.demodulize} to #{new_column.class.name.demodulize}"
53
+ end
54
+ update_column(old_column, new_column)
55
+ end
56
+ end
57
+ end
58
+
59
+ def add_column(column)
60
+ updater.add_data_column(column)
61
+ if column.indexed?
62
+ updater.create_index(column.name, column.index_name)
63
+ end
64
+ end
65
+
66
+ def update_column(old_column, new_column)
67
+ if old_column.type != new_column.type
68
+ updater.change_column(new_column.name, new_column.type)
69
+ end
70
+ if !old_column.indexed? && new_column.indexed?
71
+ updater.create_index(new_column.name, new_column.index_name)
72
+ elsif old_column.indexed? && !new_column.indexed?
73
+ updater.drop_index(old_column.index_name)
74
+ end
75
+ end
76
+
77
+ def update_properties
78
+ changes = {}
79
+ updated.properties.each_pair do |name, new_property|
80
+ old_property = existing.property(name)
81
+ if old_property != new_property.value
82
+ changes[name] = new_property.value
83
+ end
84
+ end
85
+ updater.change_properties(changes) if changes.any?
86
+ end
87
+
88
+ def each_key_pair(&block)
89
+ if existing.partition_keys.length != updated.partition_keys.length
90
+ raise InvalidSchemaMigration,
91
+ "Existing partition keys #{existing.partition_keys.map { |key| key.name }.join(',')} differ from specified partition keys #{updated.partition_keys.map { |key| key.name }.join(',')}"
92
+ end
93
+ if existing.clustering_columns.length != updated.clustering_columns.length
94
+ raise InvalidSchemaMigration,
95
+ "Existing clustering keys #{existing.clustering_columns.map { |key| key.name }.join(',')} differ from specified clustering keys #{updated.clustering_columns.map { |key| key.name }.join(',')}"
96
+ end
97
+ existing.partition_keys.zip(updated.partition_keys, &block)
98
+ existing.clustering_columns.zip(updated.clustering_columns, &block)
99
+ end
100
+
101
+ def each_column_pair(&block)
102
+ old_columns = existing.data_columns.index_by { |col| col.name }
103
+ new_columns = updated.data_columns.index_by { |col| col.name }
104
+ all_column_names = (old_columns.keys + new_columns.keys).uniq!
105
+ all_column_names.each do |name|
106
+ yield old_columns[name], new_columns[name]
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+
114
+ end
@@ -0,0 +1,83 @@
1
+ module Cequel
2
+
3
+ module Schema
4
+
5
+ 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
11
+ end
12
+
13
+ def initialize(keyspace, table_name)
14
+ @keyspace, @table_name = keyspace, table_name
15
+ @statements = []
16
+ end
17
+ private_class_method :new
18
+
19
+ def apply
20
+ statements.each { |statement| keyspace.execute(statement) }
21
+ end
22
+
23
+ def add_column(name, type)
24
+ add_data_column(Column.new(name, type))
25
+ end
26
+
27
+ def add_list(name, type)
28
+ add_data_column(List.new(name, type))
29
+ end
30
+
31
+ def add_set(name, type)
32
+ add_data_column(Set.new(name, type))
33
+ end
34
+
35
+ def add_map(name, key_type, value_type)
36
+ add_data_column(Map.new(name, key_type, value_type))
37
+ end
38
+
39
+ def change_column(name, type)
40
+ alter_table("ALTER #{name} TYPE #{type.cql_name}")
41
+ end
42
+
43
+ def rename_column(old_name, new_name)
44
+ alter_table("RENAME #{old_name} TO #{new_name}")
45
+ end
46
+
47
+ def change_properties(options)
48
+ properties = options.
49
+ map { |name, value| TableProperty.new(name, value).to_cql }
50
+ alter_table("WITH #{properties.join(' AND ')}")
51
+ end
52
+
53
+ def create_index(column, index_name)
54
+ index_name ||= "#{table_name}_#{column}_idx"
55
+ statements << "CREATE INDEX #{index_name} ON #{table_name} (#{column})"
56
+ end
57
+
58
+ def drop_index(index_name)
59
+ statements << "DROP INDEX #{index_name}"
60
+ end
61
+
62
+ def add_data_column(column)
63
+ add_column_statement(column)
64
+ end
65
+
66
+ protected
67
+ attr_reader :keyspace, :table_name, :statements
68
+
69
+ private
70
+
71
+ def alter_table(statement)
72
+ statements << "ALTER TABLE #{table_name} #{statement}"
73
+ end
74
+
75
+ def add_column_statement(column)
76
+ alter_table("ADD #{column.to_cql}")
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,80 @@
1
+ module Cequel
2
+
3
+ module Schema
4
+
5
+
6
+ class TableWriter
7
+
8
+ def self.apply(keyspace, table)
9
+ new(keyspace, table).apply
10
+ end
11
+
12
+ def initialize(keyspace, table)
13
+ @keyspace, @table = keyspace, table
14
+ end
15
+ private_class_method :new
16
+
17
+ def apply
18
+ keyspace.execute(create_statement)
19
+ index_statements.each { |statement| keyspace.execute(statement) }
20
+ end
21
+
22
+ protected
23
+ attr_reader :keyspace, :table
24
+
25
+ private
26
+
27
+ def create_statement
28
+ "CREATE TABLE #{table.name} (#{columns_cql}, #{keys_cql})".tap do |cql|
29
+ properties = properties_cql
30
+ cql << " WITH #{properties}" if properties
31
+ end
32
+ end
33
+
34
+ def index_statements
35
+ [].tap do |statements|
36
+ table.data_columns.each do |column|
37
+ if column.indexed?
38
+ statements <<
39
+ "CREATE INDEX #{column.index_name} ON #{table.name} (#{column.name})"
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def columns_cql
46
+ table.columns.map(&:to_cql).join(', ')
47
+ end
48
+
49
+ def key_columns_cql
50
+ table.keys.map(&:to_cql).join(', ')
51
+ end
52
+
53
+ def keys_cql
54
+ partition_cql = table.partition_keys.map { |key| key.name }.join(', ')
55
+ if table.clustering_columns.any?
56
+ nonpartition_cql =
57
+ table.clustering_columns.map { |key| key.name }.join(', ')
58
+ "PRIMARY KEY ((#{partition_cql}), #{nonpartition_cql})"
59
+ else
60
+ "PRIMARY KEY ((#{partition_cql}))"
61
+ end
62
+ end
63
+
64
+ def properties_cql
65
+ properties_fragments = table.properties.
66
+ map { |_, property| property.to_cql }
67
+ properties_fragments << 'COMPACT STORAGE' if table.compact_storage?
68
+ if table.clustering_columns.any?
69
+ clustering_fragment =
70
+ table.clustering_columns.map(&:clustering_order_cql).join(',')
71
+ properties_fragments << "CLUSTERING ORDER BY (#{clustering_fragment})"
72
+ end
73
+ properties_fragments.join(' AND ') if properties_fragments.any?
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,60 @@
1
+ module Cequel
2
+
3
+ module Schema
4
+
5
+ class UpdateTableDSL < BasicObject
6
+
7
+ def self.apply(updater, &block)
8
+ dsl = new(updater)
9
+ dsl.instance_eval(&block)
10
+ end
11
+
12
+ def initialize(updater)
13
+ @updater = updater
14
+ end
15
+
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
44
+ alias_method :change_options, :change_properties
45
+
46
+ def create_index(column_name, index_name = nil)
47
+ @updater.create_index(column_name, index_name)
48
+ end
49
+ alias_method :add_index, :create_index
50
+
51
+ def drop_index(index_name)
52
+ @updater.drop_index(index_name)
53
+ end
54
+ alias_method :remove_index, :drop_index
55
+
56
+ end
57
+
58
+ end
59
+
60
+ end