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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +20 -8
  4. data/config/schema.xml.erb +22 -19
  5. data/config/solrconfig.xml.erb +1 -1
  6. data/lib/cql-rb_extensions.rb +27 -0
  7. data/lib/datastax_rails.rb +13 -17
  8. data/lib/datastax_rails/associations/association.rb +1 -4
  9. data/lib/datastax_rails/associations/collection_proxy.rb +0 -13
  10. data/lib/datastax_rails/attribute_assignment.rb +28 -91
  11. data/lib/datastax_rails/attribute_methods.rb +109 -44
  12. data/lib/datastax_rails/attribute_methods/before_type_cast.rb +71 -0
  13. data/lib/datastax_rails/attribute_methods/dirty.rb +52 -11
  14. data/lib/datastax_rails/attribute_methods/primary_key.rb +87 -0
  15. data/lib/datastax_rails/attribute_methods/read.rb +120 -0
  16. data/lib/datastax_rails/attribute_methods/typecasting.rb +52 -21
  17. data/lib/datastax_rails/attribute_methods/write.rb +59 -0
  18. data/lib/datastax_rails/base.rb +227 -236
  19. data/lib/datastax_rails/cassandra_only_model.rb +25 -19
  20. data/lib/datastax_rails/column.rb +384 -0
  21. data/lib/datastax_rails/connection.rb +12 -13
  22. data/lib/datastax_rails/cql/alter_column_family.rb +0 -1
  23. data/lib/datastax_rails/cql/base.rb +15 -3
  24. data/lib/datastax_rails/cql/column_family.rb +2 -2
  25. data/lib/datastax_rails/cql/create_column_family.rb +7 -18
  26. data/lib/datastax_rails/cql/delete.rb +4 -9
  27. data/lib/datastax_rails/cql/insert.rb +2 -8
  28. data/lib/datastax_rails/cql/select.rb +4 -4
  29. data/lib/datastax_rails/cql/update.rb +8 -17
  30. data/lib/datastax_rails/dynamic_model.rb +98 -0
  31. data/lib/datastax_rails/payload_model.rb +19 -31
  32. data/lib/datastax_rails/persistence.rb +39 -54
  33. data/lib/datastax_rails/railtie.rb +1 -0
  34. data/lib/datastax_rails/reflection.rb +1 -1
  35. data/lib/datastax_rails/relation.rb +20 -20
  36. data/lib/datastax_rails/relation/batches.rb +18 -16
  37. data/lib/datastax_rails/relation/facet_methods.rb +1 -1
  38. data/lib/datastax_rails/relation/finder_methods.rb +6 -10
  39. data/lib/datastax_rails/relation/search_methods.rb +62 -48
  40. data/lib/datastax_rails/rsolr_client_wrapper.rb +1 -1
  41. data/lib/datastax_rails/schema/cassandra.rb +34 -62
  42. data/lib/datastax_rails/schema/migrator.rb +9 -24
  43. data/lib/datastax_rails/schema/solr.rb +13 -30
  44. data/lib/datastax_rails/schema_cache.rb +67 -0
  45. data/lib/datastax_rails/timestamps.rb +84 -11
  46. data/lib/datastax_rails/types/dirty_collection.rb +88 -0
  47. data/lib/datastax_rails/types/dynamic_list.rb +14 -0
  48. data/lib/datastax_rails/types/dynamic_map.rb +32 -0
  49. data/lib/datastax_rails/types/dynamic_set.rb +10 -0
  50. data/lib/datastax_rails/util/solr_repair.rb +4 -5
  51. data/lib/datastax_rails/validations.rb +6 -12
  52. data/lib/datastax_rails/validations/uniqueness.rb +0 -4
  53. data/lib/datastax_rails/version.rb +1 -1
  54. data/lib/datastax_rails/wide_storage_model.rb +13 -29
  55. data/lib/schema_migration.rb +4 -0
  56. data/spec/datastax_rails/associations_spec.rb +0 -1
  57. data/spec/datastax_rails/attribute_methods_spec.rb +9 -6
  58. data/spec/datastax_rails/base_spec.rb +26 -0
  59. data/spec/datastax_rails/column_spec.rb +238 -0
  60. data/spec/datastax_rails/cql/select_spec.rb +1 -1
  61. data/spec/datastax_rails/cql/update_spec.rb +2 -2
  62. data/spec/datastax_rails/persistence_spec.rb +29 -15
  63. data/spec/datastax_rails/relation/batches_spec.rb +5 -5
  64. data/spec/datastax_rails/relation/finder_methods_spec.rb +0 -20
  65. data/spec/datastax_rails/relation/search_methods_spec.rb +8 -0
  66. data/spec/datastax_rails/relation_spec.rb +7 -0
  67. data/spec/datastax_rails/schema/migrator_spec.rb +5 -10
  68. data/spec/datastax_rails/schema/solr_spec.rb +1 -1
  69. data/spec/datastax_rails/types/dynamic_list_spec.rb +20 -0
  70. data/spec/datastax_rails/types/dynamic_map_spec.rb +22 -0
  71. data/spec/datastax_rails/types/dynamic_set_spec.rb +16 -0
  72. data/spec/dummy/config/application.rb +2 -1
  73. data/spec/dummy/config/datastax.yml +6 -3
  74. data/spec/dummy/config/environments/development.rb +4 -5
  75. data/spec/dummy/config/environments/test.rb +0 -5
  76. data/spec/dummy/log/development.log +18 -0
  77. data/spec/dummy/log/test.log +36 -0
  78. data/spec/feature/dynamic_fields_spec.rb +9 -0
  79. data/spec/feature/overloaded_tables_spec.rb +24 -0
  80. data/spec/spec_helper.rb +1 -1
  81. data/spec/support/default_consistency_shared_examples.rb +2 -2
  82. data/spec/support/models.rb +28 -14
  83. metadata +212 -188
  84. data/lib/datastax_rails/identity.rb +0 -64
  85. data/lib/datastax_rails/identity/abstract_key_factory.rb +0 -29
  86. data/lib/datastax_rails/identity/custom_key_factory.rb +0 -37
  87. data/lib/datastax_rails/identity/hashed_natural_key_factory.rb +0 -10
  88. data/lib/datastax_rails/identity/natural_key_factory.rb +0 -39
  89. data/lib/datastax_rails/identity/uuid_key_factory.rb +0 -27
  90. data/lib/datastax_rails/type.rb +0 -16
  91. data/lib/datastax_rails/types.rb +0 -9
  92. data/lib/datastax_rails/types/array_type.rb +0 -86
  93. data/lib/datastax_rails/types/base_type.rb +0 -42
  94. data/lib/datastax_rails/types/binary_type.rb +0 -19
  95. data/lib/datastax_rails/types/boolean_type.rb +0 -22
  96. data/lib/datastax_rails/types/date_type.rb +0 -23
  97. data/lib/datastax_rails/types/float_type.rb +0 -18
  98. data/lib/datastax_rails/types/integer_type.rb +0 -18
  99. data/lib/datastax_rails/types/string_type.rb +0 -16
  100. data/lib/datastax_rails/types/text_type.rb +0 -15
  101. data/lib/datastax_rails/types/time_type.rb +0 -23
  102. data/spec/datastax_rails/types/float_type_spec.rb +0 -31
  103. data/spec/datastax_rails/types/integer_type_spec.rb +0 -31
  104. data/spec/datastax_rails/types/time_type_spec.rb +0 -28
@@ -4,7 +4,6 @@ module DatastaxRails#:nodoc:
4
4
  def initialize(cf_name)
5
5
  @cf_name = cf_name
6
6
  @action = nil
7
- @consistency = 'QUORUM'
8
7
  end
9
8
 
10
9
  def add(column)
@@ -3,15 +3,21 @@ module DatastaxRails
3
3
  class Base
4
4
  # Base initialize that sets the default consistency.
5
5
  def initialize(klass, *args)
6
- @consistency = klass.default_consistency.to_s.upcase
6
+ @consistency = klass.default_consistency.to_s.downcase.to_sym
7
7
  @keyspace = DatastaxRails::Base.config[:keyspace]
8
+ @values = []
9
+ end
10
+
11
+ def using(consistency)
12
+ @consistency = consistency.to_s.downcase.to_sym
13
+ self
8
14
  end
9
15
 
10
16
  def key_name
11
17
  @klass.key_factory.key_columns
12
18
  end
13
19
 
14
- # Abstract. Should be overridden by subclasses
20
+ # Abstract. Should be overridden by subclasses
15
21
  def to_cql
16
22
  raise NotImplementedError
17
23
  end
@@ -22,7 +28,13 @@ module DatastaxRails
22
28
  def execute
23
29
  cql = self.to_cql
24
30
  puts cql if ENV['DEBUG_CQL'] == 'true'
25
- DatastaxRails::Base.connection.execute_cql_query(cql, :consistency => CassandraCQL::Thrift::ConsistencyLevel.const_get(@consistency || 'QUORUM'))
31
+ pp @values if ENV['DEBUG_CQL'] == 'true'
32
+ if(@values.present?)
33
+ stmt = DatastaxRails::Base.connection.prepare(cql)
34
+ stmt.execute(*@values, :consistency => @consistency)
35
+ else
36
+ DatastaxRails::Base.connection.execute(cql, :consistency => @consistency)
37
+ end
26
38
  end
27
39
  end
28
40
  end
@@ -30,8 +30,8 @@ module DatastaxRails
30
30
  DatastaxRails::Cql::Truncate.new(@klass)
31
31
  end
32
32
 
33
- def update(*keys)
34
- DatastaxRails::Cql::Update.new(@klass, keys.flatten)
33
+ def update(key)
34
+ DatastaxRails::Cql::Update.new(@klass, key)
35
35
  end
36
36
  end
37
37
  end
@@ -5,22 +5,11 @@ module DatastaxRails#:nodoc:
5
5
  @cf_name = cf_name
6
6
  @columns = {}
7
7
  @storage_parameters = []
8
- @key_type = 'uuid'
9
- @key_columns = @key_name = "key"
8
+ @primary_key = 'id'
10
9
  end
11
-
12
- def key_type(key_type)
13
- @key_type = key_type
14
- self
15
- end
16
-
17
- def key_name(key_name)
18
- @key_name = key_name
19
- self
20
- end
21
-
22
- def key_columns(key_columns)
23
- @key_columns = key_columns
10
+
11
+ def primary_key(pk)
12
+ @primary_key = pk
24
13
  self
25
14
  end
26
15
 
@@ -48,14 +37,14 @@ module DatastaxRails#:nodoc:
48
37
  end
49
38
 
50
39
  def to_cql
51
- stmt = "CREATE COLUMNFAMILY #{@cf_name} (#{@key_name} #{@key_type}, "
40
+ stmt = "CREATE COLUMNFAMILY #{@cf_name} ("
52
41
  @columns.each do |name,type|
53
42
  stmt << "#{name} #{type}, "
54
43
  end
55
- stmt << "PRIMARY KEY (#{@key_columns}))"
44
+ stmt << "PRIMARY KEY (#{@primary_key}))"
56
45
  unless @storage_parameters.empty?
57
46
  stmt << " WITH "
58
- stmt << @storage_parameters.join(" AND ")
47
+ stmt << @storage_parameters.flatten.join(" AND ")
59
48
  end
60
49
 
61
50
  stmt
@@ -7,15 +7,10 @@ module DatastaxRails
7
7
  @timestamp = nil
8
8
  @columns = []
9
9
  @conditions = {}
10
- @key_name = "key"
10
+ @key_name = @klass.primary_key
11
11
  super
12
12
  end
13
13
 
14
- def using(consistency)
15
- @consistency = consistency
16
- self
17
- end
18
-
19
14
  def columns(columns)
20
15
  @columns = columns
21
16
  self
@@ -37,7 +32,7 @@ module DatastaxRails
37
32
  end
38
33
 
39
34
  def to_cql
40
- values = [@keys.collect{|k|k.to_s}]
35
+ @values = @keys
41
36
  stmt = "DELETE #{@columns.join(',')} FROM #{@klass.column_family} "
42
37
 
43
38
  if(@timestamp)
@@ -48,10 +43,10 @@ module DatastaxRails
48
43
 
49
44
  @conditions.each do |col,val|
50
45
  stmt << " AND #{col} = ?"
51
- values << val
46
+ @values << val
52
47
  end
53
48
 
54
- CassandraCQL::Statement.sanitize(stmt, values)
49
+ stmt
55
50
  end
56
51
  end
57
52
  end
@@ -9,11 +9,6 @@ module DatastaxRails
9
9
  super
10
10
  end
11
11
 
12
- def using(consistency)
13
- @consistency = consistency
14
- self
15
- end
16
-
17
12
  def columns(columns)
18
13
  @columns.merge!(columns)
19
14
  self
@@ -30,11 +25,10 @@ module DatastaxRails
30
25
  end
31
26
 
32
27
  def to_cql
33
- values = []
34
28
  keys = []
35
29
  @columns.each do |k,v|
36
30
  keys << k.to_s
37
- values << v
31
+ @values << v
38
32
  end
39
33
  stmt = "INSERT INTO #{@klass.column_family} (#{keys.join(',')}) VALUES (#{('?'*keys.size).split(//).join(',')}) "
40
34
 
@@ -46,7 +40,7 @@ module DatastaxRails
46
40
  stmt << "AND TIMESTAMP #{@timestamp}"
47
41
  end
48
42
 
49
- CassandraCQL::Statement.sanitize(stmt, values).force_encoding('UTF-8')
43
+ stmt.force_encoding('UTF-8')
50
44
  end
51
45
  end
52
46
  end
@@ -44,15 +44,15 @@ module DatastaxRails#:nodoc:
44
44
 
45
45
  def to_cql
46
46
  conditions = []
47
- values = []
48
47
  stmt = "SELECT #{@select} FROM #{@klass.column_family} "
49
48
 
50
49
  if @paginate
51
- conditions << "token(key) > token('#{@paginate}')"
50
+ conditions << "token(#{@klass.primary_key}) > token(?)"
51
+ @values << @paginate
52
52
  end
53
53
 
54
54
  @conditions.each do |k,v|
55
- values << v
55
+ @values << v
56
56
  if v.kind_of?(Array)
57
57
  conditions << "\"#{k.to_s}\" IN (?)"
58
58
  else
@@ -76,7 +76,7 @@ module DatastaxRails#:nodoc:
76
76
  stmt << "ALLOW FILTERING "
77
77
  end
78
78
 
79
- CassandraCQL::Statement.sanitize(stmt, values)
79
+ stmt
80
80
  end
81
81
  end
82
82
  end
@@ -8,11 +8,6 @@ module DatastaxRails
8
8
  super
9
9
  end
10
10
 
11
- def using(consistency)
12
- @consistency = consistency
13
- self
14
- end
15
-
16
11
  def columns(columns)
17
12
  @columns.merge!(columns)
18
13
  self
@@ -36,7 +31,6 @@ module DatastaxRails
36
31
  def to_cql
37
32
  column_names = @columns.keys
38
33
 
39
-
40
34
  stmt = "update #{@klass.column_family} "
41
35
 
42
36
  if(@ttl)
@@ -49,22 +43,19 @@ module DatastaxRails
49
43
 
50
44
  unless @columns.empty?
51
45
  stmt << "SET "
52
-
53
- first_entry = column_names.first
54
-
55
- stmt << CassandraCQL::Statement.sanitize("\"#{first_entry.to_s}\" = ?", [@columns[first_entry]])
56
- column_names[1..-1].each do |col|
57
- stmt << CassandraCQL::Statement.sanitize(", \"#{col.to_s}\" = ?", [@columns[col]])
46
+ updates = []
47
+ @columns.each do |k,v|
48
+ @values << v
49
+ updates << "\"#{k}\" = ?"
58
50
  end
51
+
52
+ stmt << updates.join(", ")
59
53
  end
60
54
 
61
- stmt << CassandraCQL::Statement.sanitize(" WHERE key IN (?)", [@key])
55
+ stmt << " WHERE #{@klass.primary_key} IN (?)"
56
+ @values << @key
62
57
  stmt.force_encoding('UTF-8')
63
58
  end
64
-
65
- # def execute
66
- # puts to_cql.truncate(50)
67
- # end
68
59
  end
69
60
  end
70
61
  end
@@ -0,0 +1,98 @@
1
+ module DatastaxRails
2
+ # An extension to Wide Storage Models that let you index any arbitrary
3
+ # field that you want (given certain naming conventions).
4
+ #
5
+ # Try to keep the group_by as sort as possible since it will get stored
6
+ # with every attribute. Static attributes are only supported if they
7
+ # are included on every dynamic model that uses the same column family.
8
+ #
9
+ # Dynamic models have the following attributes:
10
+ # * strings
11
+ # * texts
12
+ # * booleans
13
+ # * dates
14
+ # * timestamps
15
+ # * integers
16
+ # * floats
17
+ # * uuids
18
+ #
19
+ # Each of these is a map that let's you store key/value pairs where the
20
+ # key is always a String and the value is a type that matches what would
21
+ # be stored in a static attribute of the same time. Everything will get
22
+ # typecasted, so you can safely store strings in it in all the same cases
23
+ # that you store strings in normal attributes.
24
+ #
25
+ # The advantage here is that you don't have to pre-define your schema
26
+ # ahead of time. The keys of any attributes added to this collection become
27
+ # fields in your Solr document.
28
+ #
29
+ # NOTE: due to the way fields dynamically map between Solr and Cassandra,
30
+ # the field name in Solr will have a prefix prepended to it. With the
31
+ # exception of timestamps, it is simply the first letter of the type
32
+ # followed by an underscore (_). So s_ for strings. Timestamp has a
33
+ # ts_ prefix to differentiate it from texts.
34
+ #
35
+ # class Item < DatastaxRails::DynamicModel
36
+ # self.group_by = 'item'
37
+ # timestamps
38
+ # end
39
+ #
40
+ # class CoreMetadata < DatastaxRails::DynamicModel
41
+ # self.group_by = 'core'
42
+ # timestamps
43
+ # end
44
+ #
45
+ # class TeamMetadata < DatastaxRails::DynamicModel
46
+ # self.group_by = 'team'
47
+ # timestamps
48
+ # end
49
+ #
50
+ # item = Item.create(strings: {title: "Title"})
51
+ # CoreMetadata.create(id: item.id, strings: {author: 'John'}, dates: {published_on: Date.today})
52
+ # TeamMetadata.create(id: item.id, booleans: {reviewed: true})
53
+ #
54
+ # CoreMetadata.where(s_author: 'John') #=> Finds the CoreMetadata record
55
+ # Item.fulltext("Title") #=> Finds the Item record
56
+ # Item.fulltext("John") #=> Doesn't find a record, but...
57
+ # Item.fulltext("{!join from=id to=id}John") #=> Does find the record by doing a Solr join across the entire row
58
+ #
59
+ # NOTE that the mapping of key names is happening automatically when you insert something into
60
+ # the collection so:
61
+ #
62
+ # Item.first.strings #=> {s_title: "Title"}
63
+ class DynamicModel < WideStorageModel
64
+ self.abstract_class = true
65
+
66
+ class_attribute :group_by_attribute
67
+
68
+ def self.group_by=(group)
69
+ self.group_by_attribute = group
70
+ self.attribute_definitions['group'].default = group
71
+ default_scope -> {where('group' => group)}
72
+ end
73
+
74
+
75
+ def self.inherited(child)
76
+ super
77
+ child.column_family = 'dynamic_model'
78
+ child.primary_key = 'id'
79
+ child.cluster_by = 'group'
80
+ child.uuid :id
81
+ child.string :group
82
+ child.map :s_, :holds => :string
83
+ child.map :t_, :holds => :text
84
+ child.map :b_, :holds => :boolean
85
+ child.map :d_, :holds => :date
86
+ child.map :ts_, :holds => :timestamp
87
+ child.map :i_, :holds => :integer
88
+ child.map :f_, :holds => :float
89
+ child.map :u_, :holds => :uuid
90
+
91
+ child.map_columns.each do |col|
92
+ child.instance_eval do
93
+ alias_attribute col.options[:holds].to_s.pluralize, col.name
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -5,7 +5,7 @@ module DatastaxRails
5
5
  # other metadata, you will need another model that points at this
6
6
  # one.
7
7
  #
8
- # class AttachmentPayload < DatastaxRails::Payload
8
+ # class AttachmentPayload < DatastaxRails::PayloadModel
9
9
  # self.column_family = 'attachment_payloads'
10
10
  #
11
11
  # validate do
@@ -14,14 +14,18 @@ module DatastaxRails
14
14
  # end
15
15
  # end
16
16
  # end
17
- class PayloadModel < CassandraOnlyModel
17
+ class PayloadModel < WideStorageModel
18
+ include CassandraOnlyModel
18
19
  self.abstract_class = true
19
20
 
20
21
  def self.inherited(child)
21
22
  super
22
- child.key :natural, :attributes => :digest
23
+ child.primary_key = 'digest'
24
+ child.cluster_by = 'chunk'
25
+ child.create_options = 'COMPACT STORAGE'
23
26
  child.string :digest
24
27
  child.binary :payload
28
+ child.integer :chunk
25
29
  child.validates :digest, :presence => true
26
30
  end
27
31
 
@@ -31,24 +35,26 @@ module DatastaxRails
31
35
  c.using(options[:consistency]) if options[:consistency]
32
36
  io = StringIO.new("","w+")
33
37
  found = false
34
- CassandraCQL::Result.new(c.execute).fetch do |row|
35
- io << Base64.decode64(row.to_hash['payload'])
38
+ chunk = 0
39
+ c.execute.each do |row|
40
+ io << Base64.decode64(row['payload'])
41
+ chunk = row['chunk']
36
42
  found = true
37
43
  end
38
44
  raise DatastaxRails::RecordNotFound unless found
39
45
  io.rewind
40
- self.instantiate(digest, {:digest => digest, :payload => io.read}, [:digest, :payload])
46
+ self.instantiate(digest, {:digest => digest, :payload => io.read, :chunk => chunk}, [:digest, :payload])
41
47
  end
42
48
 
43
- def self.write(key, attributes, options = {})
49
+ def self.write(record, options = {})
44
50
  raise ArgumentError, "'#{options[:consistency]}' is not a valid Cassandra consistency level" unless valid_consistency?(options[:consistency].to_s.upcase) if options[:consistency]
45
- c = self.cql.select("count(*)").conditions(:digest => key)
46
- count = CassandraCQL::Result.new(c.execute).fetch.to_hash["count"]
51
+ c = self.cql.select("count(*)").conditions(:digest => record.id)
52
+ count = c.execute.first["count"]
47
53
 
48
54
  i = 0
49
- io = StringIO.new(attributes['payload'])
55
+ io = StringIO.new(record.attributes['payload'])
50
56
  while chunk = io.read(1.megabyte)
51
- c = cql.insert.columns(:digest => key, :chunk => i, :payload => Base64.encode64(chunk))
57
+ c = cql.insert.columns(:digest => record.id, :chunk => i, :payload => Base64.encode64(chunk))
52
58
  c.using(options[:consistency]) if options[:consistency]
53
59
  c.execute
54
60
  i += 1
@@ -56,31 +62,13 @@ module DatastaxRails
56
62
 
57
63
  if count and count > i
58
64
  i.upto(count) do |j|
59
- c = cql.delete(key.to_s).key_name('digest').conditions(:chunk => j)
65
+ c = cql.delete(record.id).key_name('digest').conditions(:chunk => j)
60
66
  c.using(options[:consistency]) if options[:consistency]
61
67
  c.execute
62
68
  end
63
69
  end
64
70
 
65
- key
66
- end
67
-
68
- # Instantiates a new object without calling +initialize+.
69
- #
70
- # @param [String] key the primary key for the record
71
- # @param [Hash] attributes a hash containing the columns to set on the record
72
- # @param [Array] selected_attributes an array containing the attributes that were originally selected from cassandra
73
- # to build this object. Used so that we can avoid lazy-loading attributes that don't exist.
74
- # @return [DatastaxRails::Base] a model with the given attributes
75
- def self.instantiate(key, attributes, selected_attributes = [])
76
- allocate.tap do |object|
77
- object.instance_variable_set("@loaded_attributes", {}.with_indifferent_access)
78
- object.instance_variable_set("@key", parse_key(key)) if key
79
- object.instance_variable_set("@new_record", false)
80
- object.instance_variable_set("@destroyed", false)
81
- object.instance_variable_set("@attributes", attributes.with_indifferent_access)
82
- attributes.keys.each {|k| object.instance_variable_get("@loaded_attributes")[k] = true}
83
- end
71
+ record.id
84
72
  end
85
73
  end
86
74
  end