cequel 0.5.6 → 1.0.0.pre.1

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