sessionm-cassandra_object 2.2.51 → 2.2.52

Sign up to get free protection for your applications and to get access to all the features.
@@ -25,6 +25,7 @@ module CassandraObject
25
25
  autoload :Type
26
26
  autoload :Schema
27
27
  autoload :RowTTL
28
+ autoload :NestedAttributes
28
29
 
29
30
  module Tasks
30
31
  extend ActiveSupport::Autoload
@@ -43,6 +43,7 @@ module CassandraObject
43
43
  include Batches
44
44
  include FinderMethods
45
45
  include Timestamps
46
+ include NestedAttributes
46
47
 
47
48
  attr_accessor :key
48
49
 
@@ -1,13 +1,13 @@
1
1
  module CassandraObject
2
- class CasssandraObjectError < StandardError
2
+ class CassandraObjectError < StandardError
3
3
  end
4
4
 
5
- class RecordNotSaved < CasssandraObjectError
5
+ class RecordNotSaved < CassandraObjectError
6
6
  end
7
7
 
8
- class RecordNotFound < CasssandraObjectError
8
+ class RecordNotFound < CassandraObjectError
9
9
  end
10
10
 
11
- class InvalidKey < CasssandraObjectError
11
+ class InvalidKey < CassandraObjectError
12
12
  end
13
13
  end
@@ -0,0 +1,168 @@
1
+ require 'active_support/core_ext/hash/except'
2
+ require 'active_support/core_ext/object/try'
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'active_support/core_ext/hash/indifferent_access'
5
+
6
+ module CassandraObject
7
+ module NestedAttributes #:nodoc:
8
+ class TooManyRecords < CassandraObjectError
9
+ end
10
+
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ class_inheritable_accessor :nested_attributes_options, :instance_writer => false
15
+ self.nested_attributes_options = {}
16
+ end
17
+
18
+ module ClassMethods
19
+ REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |_, value| value.blank? } }
20
+
21
+ def accepts_nested_attributes_for(*attr_names)
22
+ options = { :allow_destroy => false, :update_only => false }
23
+ options.update(attr_names.extract_options!)
24
+ options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
25
+ options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
26
+
27
+ attr_names.each do |association_name|
28
+ if reflection = reflect_on_association(association_name)
29
+ reflection.options[:autosave] = true
30
+ add_autosave_association_callbacks(reflection)
31
+ nested_attributes_options[association_name.to_sym] = options
32
+ type = (reflection.collection? ? :collection : :one_to_one)
33
+
34
+ # def pirate_attributes=(attributes)
35
+ # assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
36
+ # end
37
+ class_eval <<-eoruby, __FILE__, __LINE__ + 1
38
+ if method_defined?(:#{association_name}_attributes=)
39
+ remove_method(:#{association_name}_attributes=)
40
+ end
41
+ def #{association_name}_attributes=(attributes)
42
+ assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
43
+ end
44
+ eoruby
45
+ else
46
+ raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def _destroy
53
+ marked_for_destruction?
54
+ end
55
+
56
+ private
57
+
58
+ UNASSIGNABLE_KEYS = %w( id _destroy )
59
+
60
+ def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
61
+ options = nested_attributes_options[association_name]
62
+ attributes = attributes.with_indifferent_access
63
+ check_existing_record = (options[:update_only] || !attributes['id'].blank?)
64
+
65
+ if check_existing_record && (record = send(association_name)) &&
66
+ (options[:update_only] || record.id.to_s == attributes['id'].to_s)
67
+ assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
68
+
69
+ elsif !attributes['id'].blank?
70
+ raise_nested_attributes_record_not_found(association_name, attributes['id'])
71
+
72
+ elsif !reject_new_record?(association_name, attributes)
73
+ method = "build_#{association_name}"
74
+ if respond_to?(method)
75
+ send(method, attributes.except(*UNASSIGNABLE_KEYS))
76
+ else
77
+ raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
78
+ end
79
+ end
80
+ end
81
+
82
+ def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
83
+ options = nested_attributes_options[association_name]
84
+
85
+ unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
86
+ raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
87
+ end
88
+
89
+ if options[:limit] && attributes_collection.size > options[:limit]
90
+ raise TooManyRecords, "Maximum #{options[:limit]} records are allowed. Got #{attributes_collection.size} records instead."
91
+ end
92
+
93
+ if attributes_collection.is_a? Hash
94
+ keys = attributes_collection.keys
95
+ attributes_collection = if keys.include?('id') || keys.include?(:id)
96
+ Array.wrap(attributes_collection)
97
+ else
98
+ attributes_collection.sort_by { |i, _| i.to_i }.map { |_, attributes| attributes }
99
+ end
100
+ end
101
+
102
+ association = send(association_name)
103
+
104
+ existing_records = if association.loaded?
105
+ association.to_a
106
+ else
107
+ attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
108
+ attribute_ids.present? ? association.all(:conditions => {association.primary_key => attribute_ids}) : []
109
+ end
110
+
111
+ attributes_collection.each do |attributes|
112
+ attributes = attributes.with_indifferent_access
113
+
114
+ if attributes['id'].blank?
115
+ unless reject_new_record?(association_name, attributes)
116
+ association.build(attributes.except(*UNASSIGNABLE_KEYS))
117
+ end
118
+
119
+ elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
120
+ association.send(:add_record_to_target_with_callbacks, existing_record) if !association.loaded? && !call_reject_if(association_name, attributes)
121
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
122
+
123
+ else
124
+ raise_nested_attributes_record_not_found(association_name, attributes['id'])
125
+ end
126
+ end
127
+ end
128
+
129
+ # Updates a record with the +attributes+ or marks it for destruction if
130
+ # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
131
+ def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
132
+ record.attributes = attributes.except(*UNASSIGNABLE_KEYS)
133
+ record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
134
+ end
135
+
136
+ # Determines if a hash contains a truthy _destroy key.
137
+ def has_destroy_flag?(hash)
138
+ value = hash['_destroy']
139
+ if value.is_a?(String) && value.blank?
140
+ nil
141
+ else
142
+ [true, 1, '1', 't', 'T', 'true', 'TRUE'].include?(value)
143
+ end
144
+ end
145
+
146
+ # Determines if a new record should be build by checking for
147
+ # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
148
+ # association and evaluates to +true+.
149
+ def reject_new_record?(association_name, attributes)
150
+ has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
151
+ end
152
+
153
+ def call_reject_if(association_name, attributes)
154
+ return false if has_destroy_flag?(attributes)
155
+ case callback = nested_attributes_options[association_name][:reject_if]
156
+ when Symbol
157
+ method(callback).arity == 0 ? send(callback) : send(callback, attributes)
158
+ when Proc
159
+ callback.call(attributes)
160
+ end
161
+ end
162
+
163
+ def raise_nested_attributes_record_not_found(association_name, record_id)
164
+ reflection = self.class.reflect_on_association(association_name)
165
+ raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
166
+ end
167
+ end
168
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'sessionm-cassandra_object'
5
- s.version = '2.2.51'
5
+ s.version = '2.2.52'
6
6
  s.description = 'Cassandra ActiveModel'
7
7
  s.summary = 'Cassandra ActiveModel'
8
8
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 2
7
7
  - 2
8
- - 51
9
- version: 2.2.51
8
+ - 52
9
+ version: 2.2.52
10
10
  platform: ruby
11
11
  authors:
12
12
  - Michael Koziarski
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2012-05-21 00:00:00 -04:00
19
+ date: 2012-05-29 00:00:00 -04:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -106,6 +106,7 @@ files:
106
106
  - lib/cassandra_object/migrations.rb
107
107
  - lib/cassandra_object/migrations/migration.rb
108
108
  - lib/cassandra_object/mocking.rb
109
+ - lib/cassandra_object/nested_attributes.rb
109
110
  - lib/cassandra_object/persistence.rb
110
111
  - lib/cassandra_object/railtie.rb
111
112
  - lib/cassandra_object/row_ttl.rb