sessionm-cassandra_object 2.2.53 → 2.2.54

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.
@@ -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