sessionm-cassandra_object 2.2.53 → 2.2.54

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.53'
5
+ s.version = '2.2.54'
6
6
  s.description = 'Cassandra ActiveModel'
7
7
  s.summary = 'Cassandra ActiveModel'
8
8
 
metadata CHANGED
@@ -1,75 +1,73 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: sessionm-cassandra_object
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 2
7
- - 2
8
- - 53
9
- version: 2.2.53
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.2.54
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Michael Koziarski
13
9
  - gotime
14
10
  - sessionm
15
11
  autorequire:
16
12
  bindir: bin
17
13
  cert_chain: []
18
-
19
- date: 2012-05-31 00:00:00 -04:00
20
- default_executable:
21
- dependencies:
22
- - !ruby/object:Gem::Dependency
14
+ date: 2012-05-31 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
23
17
  name: rails
24
- prerelease: false
25
- requirement: &id001 !ruby/object:Gem::Requirement
26
- requirements:
18
+ requirement: !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
27
21
  - - ~>
28
- - !ruby/object:Gem::Version
29
- segments:
30
- - 3
31
- - 0
32
- - 9
22
+ - !ruby/object:Gem::Version
33
23
  version: 3.0.9
34
24
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: cassandra
38
25
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- segments:
44
- - 0
45
- - 11
46
- - 3
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ~>
30
+ - !ruby/object:Gem::Version
31
+ version: 3.0.9
32
+ - !ruby/object:Gem::Dependency
33
+ name: cassandra
34
+ requirement: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
47
39
  version: 0.11.3
48
40
  type: :runtime
49
- version_requirements: *id002
50
- - !ruby/object:Gem::Dependency
51
- name: bundler
52
41
  prerelease: false
53
- requirement: &id003 !ruby/object:Gem::Requirement
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- segments:
58
- - 1
59
- - 0
60
- - 0
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.11.3
48
+ - !ruby/object:Gem::Dependency
49
+ name: bundler
50
+ requirement: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
61
55
  version: 1.0.0
62
56
  type: :development
63
- version_requirements: *id003
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: 1.0.0
64
64
  description: Cassandra ActiveModel
65
65
  email: klange@sessionm.com
66
66
  executables: []
67
-
68
67
  extensions: []
69
-
70
- extra_rdoc_files:
68
+ extra_rdoc_files:
71
69
  - README.markdown
72
- files:
70
+ files:
73
71
  - .gitignore
74
72
  - CHANGELOG
75
73
  - Gemfile
@@ -106,6 +104,7 @@ files:
106
104
  - lib/cassandra_object/migrations.rb
107
105
  - lib/cassandra_object/migrations/migration.rb
108
106
  - lib/cassandra_object/mocking.rb
107
+ - lib/cassandra_object/nested_attributes.rb
109
108
  - lib/cassandra_object/persistence.rb
110
109
  - lib/cassandra_object/railtie.rb
111
110
  - lib/cassandra_object/row_ttl.rb
@@ -158,41 +157,31 @@ files:
158
157
  - test/types/time_type_test.rb
159
158
  - test/types/utf8_string_type_test.rb
160
159
  - test/validations_test.rb
161
- has_rdoc: true
162
160
  homepage: http://github.com/sessionm/cassandra_object
163
161
  licenses: []
164
-
165
162
  post_install_message:
166
163
  rdoc_options: []
167
-
168
- require_paths:
164
+ require_paths:
169
165
  - lib
170
- required_ruby_version: !ruby/object:Gem::Requirement
171
- requirements:
172
- - - ">="
173
- - !ruby/object:Gem::Version
174
- segments:
175
- - 1
176
- - 9
177
- - 2
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ none: false
168
+ requirements:
169
+ - - ! '>='
170
+ - !ruby/object:Gem::Version
178
171
  version: 1.9.2
179
- required_rubygems_version: !ruby/object:Gem::Requirement
180
- requirements:
181
- - - ">="
182
- - !ruby/object:Gem::Version
183
- segments:
184
- - 1
185
- - 3
186
- - 5
172
+ required_rubygems_version: !ruby/object:Gem::Requirement
173
+ none: false
174
+ requirements:
175
+ - - ! '>='
176
+ - !ruby/object:Gem::Version
187
177
  version: 1.3.5
188
178
  requirements: []
189
-
190
179
  rubyforge_project:
191
- rubygems_version: 1.3.6
180
+ rubygems_version: 1.8.22
192
181
  signing_key:
193
182
  specification_version: 3
194
183
  summary: Cassandra ActiveModel
195
- test_files:
184
+ test_files:
196
185
  - test/active_model_test.rb
197
186
  - test/attributes_test.rb
198
187
  - test/base_test.rb