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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -8
- data/config/schema.xml.erb +22 -19
- data/config/solrconfig.xml.erb +1 -1
- data/lib/cql-rb_extensions.rb +27 -0
- data/lib/datastax_rails.rb +13 -17
- data/lib/datastax_rails/associations/association.rb +1 -4
- data/lib/datastax_rails/associations/collection_proxy.rb +0 -13
- data/lib/datastax_rails/attribute_assignment.rb +28 -91
- data/lib/datastax_rails/attribute_methods.rb +109 -44
- data/lib/datastax_rails/attribute_methods/before_type_cast.rb +71 -0
- data/lib/datastax_rails/attribute_methods/dirty.rb +52 -11
- data/lib/datastax_rails/attribute_methods/primary_key.rb +87 -0
- data/lib/datastax_rails/attribute_methods/read.rb +120 -0
- data/lib/datastax_rails/attribute_methods/typecasting.rb +52 -21
- data/lib/datastax_rails/attribute_methods/write.rb +59 -0
- data/lib/datastax_rails/base.rb +227 -236
- data/lib/datastax_rails/cassandra_only_model.rb +25 -19
- data/lib/datastax_rails/column.rb +384 -0
- data/lib/datastax_rails/connection.rb +12 -13
- data/lib/datastax_rails/cql/alter_column_family.rb +0 -1
- data/lib/datastax_rails/cql/base.rb +15 -3
- data/lib/datastax_rails/cql/column_family.rb +2 -2
- data/lib/datastax_rails/cql/create_column_family.rb +7 -18
- data/lib/datastax_rails/cql/delete.rb +4 -9
- data/lib/datastax_rails/cql/insert.rb +2 -8
- data/lib/datastax_rails/cql/select.rb +4 -4
- data/lib/datastax_rails/cql/update.rb +8 -17
- data/lib/datastax_rails/dynamic_model.rb +98 -0
- data/lib/datastax_rails/payload_model.rb +19 -31
- data/lib/datastax_rails/persistence.rb +39 -54
- data/lib/datastax_rails/railtie.rb +1 -0
- data/lib/datastax_rails/reflection.rb +1 -1
- data/lib/datastax_rails/relation.rb +20 -20
- data/lib/datastax_rails/relation/batches.rb +18 -16
- data/lib/datastax_rails/relation/facet_methods.rb +1 -1
- data/lib/datastax_rails/relation/finder_methods.rb +6 -10
- data/lib/datastax_rails/relation/search_methods.rb +62 -48
- data/lib/datastax_rails/rsolr_client_wrapper.rb +1 -1
- data/lib/datastax_rails/schema/cassandra.rb +34 -62
- data/lib/datastax_rails/schema/migrator.rb +9 -24
- data/lib/datastax_rails/schema/solr.rb +13 -30
- data/lib/datastax_rails/schema_cache.rb +67 -0
- data/lib/datastax_rails/timestamps.rb +84 -11
- data/lib/datastax_rails/types/dirty_collection.rb +88 -0
- data/lib/datastax_rails/types/dynamic_list.rb +14 -0
- data/lib/datastax_rails/types/dynamic_map.rb +32 -0
- data/lib/datastax_rails/types/dynamic_set.rb +10 -0
- data/lib/datastax_rails/util/solr_repair.rb +4 -5
- data/lib/datastax_rails/validations.rb +6 -12
- data/lib/datastax_rails/validations/uniqueness.rb +0 -4
- data/lib/datastax_rails/version.rb +1 -1
- data/lib/datastax_rails/wide_storage_model.rb +13 -29
- data/lib/schema_migration.rb +4 -0
- data/spec/datastax_rails/associations_spec.rb +0 -1
- data/spec/datastax_rails/attribute_methods_spec.rb +9 -6
- data/spec/datastax_rails/base_spec.rb +26 -0
- data/spec/datastax_rails/column_spec.rb +238 -0
- data/spec/datastax_rails/cql/select_spec.rb +1 -1
- data/spec/datastax_rails/cql/update_spec.rb +2 -2
- data/spec/datastax_rails/persistence_spec.rb +29 -15
- data/spec/datastax_rails/relation/batches_spec.rb +5 -5
- data/spec/datastax_rails/relation/finder_methods_spec.rb +0 -20
- data/spec/datastax_rails/relation/search_methods_spec.rb +8 -0
- data/spec/datastax_rails/relation_spec.rb +7 -0
- data/spec/datastax_rails/schema/migrator_spec.rb +5 -10
- data/spec/datastax_rails/schema/solr_spec.rb +1 -1
- data/spec/datastax_rails/types/dynamic_list_spec.rb +20 -0
- data/spec/datastax_rails/types/dynamic_map_spec.rb +22 -0
- data/spec/datastax_rails/types/dynamic_set_spec.rb +16 -0
- data/spec/dummy/config/application.rb +2 -1
- data/spec/dummy/config/datastax.yml +6 -3
- data/spec/dummy/config/environments/development.rb +4 -5
- data/spec/dummy/config/environments/test.rb +0 -5
- data/spec/dummy/log/development.log +18 -0
- data/spec/dummy/log/test.log +36 -0
- data/spec/feature/dynamic_fields_spec.rb +9 -0
- data/spec/feature/overloaded_tables_spec.rb +24 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/default_consistency_shared_examples.rb +2 -2
- data/spec/support/models.rb +28 -14
- metadata +212 -188
- data/lib/datastax_rails/identity.rb +0 -64
- data/lib/datastax_rails/identity/abstract_key_factory.rb +0 -29
- data/lib/datastax_rails/identity/custom_key_factory.rb +0 -37
- data/lib/datastax_rails/identity/hashed_natural_key_factory.rb +0 -10
- data/lib/datastax_rails/identity/natural_key_factory.rb +0 -39
- data/lib/datastax_rails/identity/uuid_key_factory.rb +0 -27
- data/lib/datastax_rails/type.rb +0 -16
- data/lib/datastax_rails/types.rb +0 -9
- data/lib/datastax_rails/types/array_type.rb +0 -86
- data/lib/datastax_rails/types/base_type.rb +0 -42
- data/lib/datastax_rails/types/binary_type.rb +0 -19
- data/lib/datastax_rails/types/boolean_type.rb +0 -22
- data/lib/datastax_rails/types/date_type.rb +0 -23
- data/lib/datastax_rails/types/float_type.rb +0 -18
- data/lib/datastax_rails/types/integer_type.rb +0 -18
- data/lib/datastax_rails/types/string_type.rb +0 -16
- data/lib/datastax_rails/types/text_type.rb +0 -15
- data/lib/datastax_rails/types/time_type.rb +0 -23
- data/spec/datastax_rails/types/float_type_spec.rb +0 -31
- data/spec/datastax_rails/types/integer_type_spec.rb +0 -31
- data/spec/datastax_rails/types/time_type_spec.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d858bc7834309a95814f407b2c85512b50cfa7a
|
4
|
+
data.tar.gz: c8c2896838848cd1f00a9c8f662958682ec60cff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9cd869dd42101301ad8450e10e45bb983628217a079831fc834e0d65652bd87212ac8ecd90455c7c40fc1632a099f682a30786d42686fd9c4bbd8c64a312580
|
7
|
+
data.tar.gz: 44a0baf08768ac8147fc59555c6cacc3021c95c0da3138aeb0c09790bf28fbcd0ee506faee060a264e4e23be5c5aa97a8ecd1e65a6c20d98ecb9a66b0d9325c6
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -29,12 +29,13 @@ First add it to your Gemfile:
|
|
29
29
|
Configure the config/datastax.yml file:
|
30
30
|
|
31
31
|
development:
|
32
|
-
servers: ["
|
32
|
+
servers: ["127.0.0.1"]
|
33
|
+
port: 9042
|
33
34
|
keyspace: "<my_app>_development"
|
34
35
|
strategy_class: "org.apache.cassandra.locator.NetworkTopologyStrategy"
|
35
36
|
strategy_options: {"DC1": "1"}
|
36
37
|
connection_options:
|
37
|
-
timeout:
|
38
|
+
timeout: 10
|
38
39
|
solr:
|
39
40
|
port: 8983
|
40
41
|
path: /solr
|
@@ -45,12 +46,13 @@ NetworkTopologySnitch and set up the cassandra-topology.properties file. See th
|
|
45
46
|
For a more simple, single datacenter setup, something like this should probably work:
|
46
47
|
|
47
48
|
development:
|
48
|
-
servers: ["
|
49
|
-
|
49
|
+
servers: ["127.0.0.1"]
|
50
|
+
port: 9042
|
51
|
+
keyspace: "datastax_rails_development"
|
50
52
|
strategy_class: "org.apache.cassandra.locator.SimpleStrategy"
|
51
53
|
strategy_options: {"replication_factor": "1"}
|
52
54
|
connection_options:
|
53
|
-
timeout:
|
55
|
+
timeout: 10
|
54
56
|
solr:
|
55
57
|
port: 8983
|
56
58
|
path: /solr
|
@@ -73,7 +75,7 @@ attributes on any model. DSR will only upload schema files if they have changed
|
|
73
75
|
Create a sample Model. See Base documentation for more details:
|
74
76
|
|
75
77
|
class Person < DatastaxRails::Base
|
76
|
-
|
78
|
+
uuid :id
|
77
79
|
string :first_name
|
78
80
|
string :user_name
|
79
81
|
text :bio
|
@@ -81,12 +83,22 @@ Create a sample Model. See Base documentation for more details:
|
|
81
83
|
boolean :active
|
82
84
|
timestamps
|
83
85
|
end
|
86
|
+
|
87
|
+
=== Different types of models
|
88
|
+
|
89
|
+
In addition to the simple model above, there are several special case models that take advantage of special
|
90
|
+
features of Cassandra. For examples of using those, see the following classes:
|
91
|
+
|
92
|
+
* {DatastaxRails::WideStorageModel}
|
93
|
+
* {DatastaxRails::PayloadModel}
|
94
|
+
* {DatastaxRails::DynamicModel}
|
84
95
|
|
85
96
|
=== Known issues
|
86
97
|
|
87
|
-
When doing a grouped query, the total groups reported is only the total groups included in your query, not the total matching groups (if you're paginating).
|
98
|
+
When doing a grouped query, the total groups reported is only the total groups included in your query, not the total matching groups (if you're paginating).
|
99
|
+
This is due to the way solr sharding works with grouped queries.
|
88
100
|
|
89
101
|
=== More information
|
90
102
|
|
91
|
-
The documentation for DatastaxRails::Base and DatastaxRails::SearchMethods will give you quite a few examples
|
103
|
+
The documentation for {DatastaxRails::Base} and {DatastaxRails::SearchMethods} will give you quite a few examples
|
92
104
|
of things you can do. You can find a copy of the latest documentation on line at http://rdoc.info/github/jasonmk/datastax_rails/master/frames.
|
data/config/schema.xml.erb
CHANGED
@@ -36,35 +36,38 @@
|
|
36
36
|
<filter class="solr.LowerCaseFilterFactory"/>
|
37
37
|
</analyzer>
|
38
38
|
</fieldType>
|
39
|
-
<fieldType name="array" class="solr.TextField" positionIncrementGap="100">
|
40
|
-
<analyzer>
|
41
|
-
<tokenizer class="solr.PatternTokenizerFactory" pattern="\$\$\$\$"/>
|
42
|
-
<filter class="solr.LowerCaseFilterFactory"/>
|
43
|
-
</analyzer>
|
44
|
-
</fieldType>
|
45
39
|
<fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
|
46
40
|
<fieldType name="date" class="solr.TrieDateField" precisionStep="0" positionIncrementGap="0"/>
|
47
41
|
<fieldType name="int" class="solr.TrieIntField" precisionStep="0" positionIncrementGap="0"/>
|
48
42
|
<fieldType name="float" class="solr.TrieFloatField" precisionStep="0" positionIncrementGap="0"/>
|
49
43
|
<fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
|
50
44
|
<fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" positionIncrementGap="0"/>
|
45
|
+
<fieldType name="uuid" class="solr.UUIDField" indexed="true" />
|
46
|
+
<fieldType name="binary" class="solr.BinaryField" indexed="false" />
|
51
47
|
</types>
|
52
48
|
<fields>
|
53
|
-
|
54
|
-
|
55
|
-
|
49
|
+
<% @columns.each do |field| %>
|
50
|
+
<% if field.type == :map %>
|
51
|
+
<dynamicField name="<%= field.name %>*" type="<%= field.solr_type %>" indexed="<%= field.options[:solr_index] %>" stored="<%= field.options[:solr_store] %>" multiValued="false"/>
|
52
|
+
<% else %>
|
53
|
+
<field name="<%= field.name %>" type="<%= field.solr_type %>" indexed="<%= field.options[:solr_index] %>" stored="<%= field.options[:solr_store] %>" multiValued="<%= field.options[:multi_valued] %>"/>
|
54
|
+
<% if field.solr_type == 'text' && field.options[:sortable] %>
|
55
|
+
<field name="sort_<%= field.name %>" type="string" indexed="true" stored="fase" multiValued="false"/>
|
56
|
+
<% end %>
|
57
|
+
<% end %>
|
56
58
|
<% end %>
|
57
59
|
<field name="text" type="text" indexed="true" stored="false" multiValued="true"/>
|
58
60
|
</fields>
|
59
61
|
|
60
|
-
<defaultSearchField>text</defaultSearchField>
|
61
|
-
<solrQueryParser defaultOperator="AND"/>
|
62
|
-
<uniqueKey
|
63
|
-
<% @
|
64
|
-
|
65
|
-
|
66
|
-
<%
|
67
|
-
|
68
|
-
|
69
|
-
|
62
|
+
<defaultSearchField>text</defaultSearchField>
|
63
|
+
<solrQueryParser defaultOperator="AND"/>
|
64
|
+
<uniqueKey><%= @primary_key %></uniqueKey>
|
65
|
+
<% @columns.each do |field| %>
|
66
|
+
<% if field.options[:fulltext] %>
|
67
|
+
<copyField source="<%= field.name %><%= field.type == :map ? "*" : ''%>" dest="text"/>
|
68
|
+
<% end %>
|
69
|
+
<% if field.solr_type == 'text' && field.options[:sortable] %>
|
70
|
+
<copyField source="<%= field.name %>" dest="sort_<%= field.name%>"/>
|
71
|
+
<% end %>
|
72
|
+
<% end %>
|
70
73
|
</schema>
|
data/config/solrconfig.xml.erb
CHANGED
@@ -50,7 +50,7 @@
|
|
50
50
|
<luceneMatchVersion>LUCENE_40</luceneMatchVersion>
|
51
51
|
|
52
52
|
<!-- Enable DSE Search new type mappings -->
|
53
|
-
<dseTypeMappingVersion
|
53
|
+
<dseTypeMappingVersion>2</dseTypeMappingVersion>
|
54
54
|
|
55
55
|
<!-- lib directives can be used to instruct Solr to load an Jars
|
56
56
|
identified and use them to resolve any "plugins" specified in
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'cql'
|
2
|
+
require 'cql/client/connection_manager'
|
3
|
+
|
4
|
+
Cql::Client::ConnectionManager.class_eval do
|
5
|
+
attr_reader :current_connection
|
6
|
+
|
7
|
+
def random_connection
|
8
|
+
raise NotConnectedError unless connected?
|
9
|
+
@lock.synchronize do
|
10
|
+
@count ||= 0
|
11
|
+
@count += 1
|
12
|
+
if @count > 500
|
13
|
+
@count = 0
|
14
|
+
@current_connection = nil
|
15
|
+
end
|
16
|
+
@current_connection ||= @connections.sample
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'cql/client/synchronous_client'
|
22
|
+
|
23
|
+
Cql::Client::SynchronousClient.class_eval do
|
24
|
+
def current_connection
|
25
|
+
async.instance_variable_get(:@connection_manager).current_connection
|
26
|
+
end
|
27
|
+
end
|
data/lib/datastax_rails.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'active_support/all'
|
2
|
-
require 'cassandra-cql/1.2'
|
3
2
|
require 'blankslate'
|
4
3
|
require 'schema_migration'
|
5
4
|
|
@@ -14,11 +13,12 @@ module DatastaxRails
|
|
14
13
|
autoload :Batches
|
15
14
|
autoload :Callbacks
|
16
15
|
autoload :CassandraOnlyModel
|
16
|
+
autoload :Column
|
17
17
|
autoload :Collection
|
18
18
|
autoload :Connection
|
19
19
|
autoload :Cql
|
20
|
+
autoload :DynamicModel
|
20
21
|
autoload :GroupedCollection
|
21
|
-
autoload :Identity
|
22
22
|
autoload :Index
|
23
23
|
autoload :Inheritance
|
24
24
|
autoload :PayloadModel
|
@@ -41,7 +41,6 @@ module DatastaxRails
|
|
41
41
|
autoload :Scoping
|
42
42
|
autoload :Serialization
|
43
43
|
autoload :Timestamps
|
44
|
-
autoload :Type
|
45
44
|
autoload_under 'util' do
|
46
45
|
autoload :SolrRepair
|
47
46
|
end
|
@@ -53,9 +52,11 @@ module DatastaxRails
|
|
53
52
|
extend ActiveSupport::Autoload
|
54
53
|
|
55
54
|
eager_autoload do
|
56
|
-
autoload :Definition
|
57
55
|
autoload :Dirty
|
56
|
+
autoload :PrimaryKey
|
57
|
+
autoload :Read
|
58
58
|
autoload :Typecasting
|
59
|
+
autoload :Write
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
@@ -73,26 +74,21 @@ module DatastaxRails
|
|
73
74
|
autoload :Keyspace
|
74
75
|
autoload :ColumnFamily
|
75
76
|
end
|
76
|
-
|
77
|
+
|
77
78
|
module Types
|
78
79
|
extend ActiveSupport::Autoload
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
autoload :IntegerType
|
87
|
-
autoload :JsonType
|
88
|
-
autoload :StringType
|
89
|
-
autoload :TextType
|
90
|
-
autoload :TimeType
|
91
|
-
autoload :TimeWithZoneType
|
81
|
+
eager_autoload do
|
82
|
+
autoload :DirtyCollection
|
83
|
+
autoload :DynamicList
|
84
|
+
autoload :DynamicSet
|
85
|
+
autoload :DynamicMap
|
86
|
+
end
|
92
87
|
end
|
93
88
|
end
|
94
89
|
|
95
90
|
require 'datastax_rails/railtie' if defined?(Rails)
|
96
91
|
require 'datastax_rails/errors'
|
92
|
+
require 'cql-rb_extensions'
|
97
93
|
|
98
94
|
ActiveSupport.run_load_hooks(:datastax_rails, DatastaxRails::Base)
|
@@ -213,10 +213,7 @@ module DatastaxRails
|
|
213
213
|
|
214
214
|
def build_record(attributes, options)
|
215
215
|
reflection.build_association(attributes, options) do |r|
|
216
|
-
r.assign_attributes(
|
217
|
-
create_scope.except(*r.changed),
|
218
|
-
:without_protection => true
|
219
|
-
)
|
216
|
+
r.assign_attributes(create_scope.except(*r.changed))
|
220
217
|
end
|
221
218
|
end
|
222
219
|
end
|
@@ -61,19 +61,6 @@ module DatastaxRails
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def method_missing(method, *args, &block)
|
64
|
-
if Rails.version =~ /^3.*/
|
65
|
-
match = ActiveRecord::DynamicFinderMatch.match(method)
|
66
|
-
elsif Rails.version =~ /^4.*/
|
67
|
-
match = ActiveRecord::DynamicMatchers::Method.match(self, method)
|
68
|
-
end
|
69
|
-
if match && match.instantiator?
|
70
|
-
send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r|
|
71
|
-
proxy_association.send :set_owner_attributes, r
|
72
|
-
proxy_association.send :add_to_target, r
|
73
|
-
yield(r) if block_given?
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
64
|
if target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
|
78
65
|
if load_target
|
79
66
|
if target.respond_to?(method)
|
@@ -3,112 +3,49 @@ require 'active_support/concern'
|
|
3
3
|
module DatastaxRails
|
4
4
|
module AttributeAssignment
|
5
5
|
extend ActiveSupport::Concern
|
6
|
-
|
7
|
-
include ActiveModel::MassAssignmentSecurity
|
8
|
-
elsif Rails.version =~ /^4.*/
|
9
|
-
include ActiveModel::DeprecatedMassAssignmentSecurity
|
10
|
-
include ActiveModel::ForbiddenAttributesProtection
|
11
|
-
end
|
12
|
-
|
13
|
-
module ClassMethods
|
14
|
-
private
|
15
|
-
|
16
|
-
# The primary key can never be set by mass-assignment for security reasons.
|
17
|
-
def attributes_protected_by_default
|
18
|
-
["key"]
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Allows you to set all the attributes at once by passing in a hash with keys
|
23
|
-
# matching the attribute names (which again matches the column names).
|
24
|
-
#
|
25
|
-
# If any attributes are protected by either +attr_protected+ or
|
26
|
-
# +attr_accessible+ then only settable attributes will be assigned.
|
27
|
-
#
|
28
|
-
# class User < ActiveRecord::Base
|
29
|
-
# attr_protected :is_admin
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# user = User.new
|
33
|
-
# user.attributes = { :username => 'Phusion', :is_admin => true }
|
34
|
-
# user.username # => "Phusion"
|
35
|
-
# user.is_admin? # => false
|
36
|
-
def attributes=(new_attributes)
|
37
|
-
return unless new_attributes.is_a?(Hash)
|
38
|
-
|
39
|
-
assign_attributes(new_attributes)
|
40
|
-
end
|
6
|
+
include ActiveModel::ForbiddenAttributesProtection
|
41
7
|
|
42
|
-
# Allows you to set all the attributes
|
43
|
-
#
|
44
|
-
# the attribute names (which again matches the column names) and the role
|
45
|
-
# name using the :as option.
|
46
|
-
#
|
47
|
-
# To bypass mass-assignment security you can use the :without_protection => true
|
48
|
-
# option.
|
49
|
-
#
|
50
|
-
# class User < ActiveRecord::Base
|
51
|
-
# attr_accessible :name
|
52
|
-
# attr_accessible :name, :is_admin, :as => :admin
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# user = User.new
|
56
|
-
# user.assign_attributes({ :name => 'Josh', :is_admin => true })
|
57
|
-
# user.name # => "Josh"
|
58
|
-
# user.is_admin? # => false
|
8
|
+
# Allows you to set all the attributes by passing in a hash of attributes with
|
9
|
+
# keys matching the attribute names (which again matches the column names).
|
59
10
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
|
64
|
-
#
|
65
|
-
# user = User.new
|
66
|
-
# user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
|
67
|
-
# user.name # => "Josh"
|
68
|
-
# user.is_admin? # => true
|
69
|
-
def assign_attributes(new_attributes, options = {})
|
11
|
+
# If the passed hash responds to <tt>permitted?</tt> method and the return value
|
12
|
+
# of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt>
|
13
|
+
# exception is raised.
|
14
|
+
def assign_attributes(new_attributes)
|
70
15
|
return if new_attributes.blank?
|
71
16
|
|
72
17
|
attributes = new_attributes.stringify_keys
|
73
18
|
nested_parameter_attributes = []
|
74
|
-
@mass_assignment_options = options
|
75
19
|
|
76
|
-
|
77
|
-
|
78
|
-
|
20
|
+
attributes = sanitize_for_mass_assignment(attributes)
|
21
|
+
|
22
|
+
attributes.each do |k, v|
|
23
|
+
if v.is_a?(Hash)
|
24
|
+
nested_parameter_attributes << [ k, v ]
|
79
25
|
else
|
80
|
-
|
26
|
+
_assign_attribute(k, v)
|
81
27
|
end
|
82
28
|
end
|
83
29
|
|
84
|
-
|
30
|
+
assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
|
31
|
+
end
|
32
|
+
alias attributes= assign_attributes
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def _assign_attribute(k, v)
|
37
|
+
public_send("#{k}=", v)
|
38
|
+
rescue NoMethodError
|
85
39
|
if respond_to?("#{k}=")
|
86
|
-
|
87
|
-
nested_parameter_attributes << [ k, v ]
|
88
|
-
else
|
89
|
-
send("#{k}=", v)
|
90
|
-
end
|
40
|
+
raise
|
91
41
|
else
|
92
|
-
raise
|
42
|
+
raise UnknownAttributeError, "unknown attribute: #{k}"
|
93
43
|
end
|
94
44
|
end
|
95
|
-
|
96
|
-
#
|
97
|
-
|
98
|
-
|
45
|
+
|
46
|
+
# Assign any deferred nested attributes after the base attributes have been set.
|
47
|
+
def assign_nested_parameter_attributes(pairs)
|
48
|
+
pairs.each { |k, v| _assign_attribute(k, v) }
|
99
49
|
end
|
100
|
-
|
101
|
-
@mass_assignment_options = nil
|
102
|
-
end
|
103
|
-
|
104
|
-
protected
|
105
|
-
|
106
|
-
def mass_assignment_options
|
107
|
-
@mass_assignment_options ||= {}
|
108
|
-
end
|
109
|
-
|
110
|
-
def mass_assignment_role
|
111
|
-
mass_assignment_options[:as] || :default
|
112
|
-
end
|
113
50
|
end
|
114
51
|
end
|
@@ -1,24 +1,67 @@
|
|
1
|
+
require 'mutex_m'
|
2
|
+
|
1
3
|
module DatastaxRails
|
2
4
|
module AttributeMethods
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
include ActiveModel::AttributeMethods
|
5
7
|
|
6
8
|
included do
|
9
|
+
initialize_generated_modules
|
10
|
+
|
11
|
+
include Write
|
12
|
+
include Dirty
|
13
|
+
include Read
|
14
|
+
include PrimaryKey
|
15
|
+
include Typecasting
|
16
|
+
|
7
17
|
alias :[] :read_attribute
|
8
18
|
alias :[]= :write_attribute
|
9
|
-
|
10
|
-
attribute_method_suffix("=")
|
19
|
+
alias has_attribute? attribute_exists?
|
11
20
|
end
|
12
21
|
|
13
22
|
module ClassMethods
|
23
|
+
def inherited(child_class)
|
24
|
+
child_class.initialize_generated_modules
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize_generated_modules
|
29
|
+
@generated_attribute_methods = Module.new {
|
30
|
+
extend Mutex_m
|
31
|
+
|
32
|
+
const_set :AttrNames, Module.new {
|
33
|
+
def self.set_name_cache(name, value)
|
34
|
+
const_name = "ATTR_#{name}"
|
35
|
+
unless const_defined? const_name
|
36
|
+
const_set const_name, value.dup.freeze
|
37
|
+
end
|
38
|
+
end
|
39
|
+
}
|
40
|
+
}
|
41
|
+
@attribute_methods_generated = false
|
42
|
+
include @generated_attribute_methods
|
43
|
+
end
|
44
|
+
|
14
45
|
def define_attribute_methods
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
46
|
+
# Use a mutex; we don't want two thread simultaneously trying to define
|
47
|
+
# attribute methods.
|
48
|
+
generated_attribute_methods.synchronize do
|
49
|
+
return false if attribute_methods_generated?
|
50
|
+
super(attribute_definitions.keys)
|
51
|
+
# Remove setter methods from readonly attributes
|
52
|
+
readonly_attributes.each do |attr|
|
53
|
+
remove_method("#{attr}=".to_sym) if method_defined?("#{attr}=".to_sym)
|
54
|
+
end
|
55
|
+
@attribute_methods_generated = true
|
56
|
+
end
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def undefine_attribute_methods
|
61
|
+
generated_attribute_methods.synchronize do
|
62
|
+
super if attribute_methods_generated?
|
63
|
+
@attribute_methods_generated = false
|
20
64
|
end
|
21
|
-
@attribute_methods_generated = true
|
22
65
|
end
|
23
66
|
|
24
67
|
def attribute_methods_generated?
|
@@ -32,21 +75,8 @@ module DatastaxRails
|
|
32
75
|
def attribute(name, options)
|
33
76
|
type = options.delete :type
|
34
77
|
coder = options.delete :coder
|
35
|
-
|
36
|
-
if self <= CassandraOnlyModel
|
37
|
-
if options[:indexed] == :both || options[:indexed] == :cassandra
|
38
|
-
options[:indexed] = :cassandra
|
39
|
-
else
|
40
|
-
options[:indexed] = false
|
41
|
-
end
|
42
|
-
end
|
78
|
+
default = options.delete :default
|
43
79
|
|
44
|
-
if type.is_a?(Symbol)
|
45
|
-
coder = DatastaxRails::Type.get_coder(type) || (raise "Unknown type #{type}")
|
46
|
-
elsif coder.nil?
|
47
|
-
raise "Must supply a :coder for #{name}"
|
48
|
-
end
|
49
|
-
|
50
80
|
if(options[:lazy])
|
51
81
|
lazy_attributes << name.to_sym
|
52
82
|
end
|
@@ -54,31 +84,48 @@ module DatastaxRails
|
|
54
84
|
if(options[:readonly])
|
55
85
|
readonly_attributes << name.to_sym
|
56
86
|
end
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
87
|
+
|
88
|
+
column = Column.new(name, default, type, options)
|
89
|
+
column.primary = (name.to_s == primary_key.to_s)
|
90
|
+
if coder
|
91
|
+
coder = coder.constantize rescue nil
|
92
|
+
if coder.class == Class && (coder.instance_methods & [:dump, :load]).size == 2
|
93
|
+
column.coder = coder.new(self)
|
94
|
+
else
|
95
|
+
raise ArgumentError, "Coder must be a class that responds the dump and load instance variables"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
attribute_definitions[name.to_sym] = column
|
68
99
|
end
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
100
|
+
|
101
|
+
# Returns the column object for the named attribute. Returns +nil+ if the
|
102
|
+
# named attribute not exists.
|
103
|
+
#
|
104
|
+
# class Person < DatastaxRails::Base
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# person = Person.new
|
108
|
+
# person.column_for_attribute(:name)
|
109
|
+
# # => #<DatastaxRails::Base:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
110
|
+
#
|
111
|
+
# person.column_for_attribute(:nothing)
|
112
|
+
# # => nil
|
113
|
+
def column_for_attribute(name)
|
114
|
+
# FIXME: should this return a null object for columns that don't exist?
|
115
|
+
column = self.columns_hash[name.to_s]
|
116
|
+
unless column
|
117
|
+
# Check for a dynamic column
|
118
|
+
columns_hash.values.each do |col|
|
119
|
+
if col.type == :map && name.to_s.starts_with?("#{col.name}")
|
120
|
+
column = col
|
121
|
+
break
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
column
|
77
126
|
end
|
78
|
-
|
79
|
-
@attributes[name.to_s]
|
80
127
|
end
|
81
|
-
|
128
|
+
|
82
129
|
def attribute_exists?(name)
|
83
130
|
@attributes.key?(name.to_s)
|
84
131
|
end
|
@@ -96,11 +143,29 @@ module DatastaxRails
|
|
96
143
|
self.class.define_attribute_methods unless self.class.attribute_methods_generated?
|
97
144
|
super
|
98
145
|
end
|
146
|
+
|
147
|
+
def column_for_attribute(name)
|
148
|
+
self.class.column_for_attribute(name)
|
149
|
+
end
|
99
150
|
|
100
151
|
protected
|
101
152
|
def attribute_method?(name)
|
102
153
|
!!attribute_definitions[name.to_sym]
|
103
154
|
end
|
155
|
+
|
156
|
+
def clone_attributes(reader_method = :read_attribute, attributes = {}) # :nodoc:
|
157
|
+
attribute_names.each do |name|
|
158
|
+
attributes[name] = clone_attribute_value(reader_method, name)
|
159
|
+
end
|
160
|
+
attributes
|
161
|
+
end
|
162
|
+
|
163
|
+
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
|
164
|
+
value = send(reader_method, attribute_name)
|
165
|
+
value.duplicable? ? value.clone : value
|
166
|
+
rescue TypeError, NoMethodError
|
167
|
+
value
|
168
|
+
end
|
104
169
|
|
105
170
|
private
|
106
171
|
def attribute(name)
|