couchrest_model 1.0.0.beta8 → 1.0.0
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.
- data/.gitignore +9 -0
- data/{spec/spec.opts → .rspec} +0 -1
- data/Gemfile +4 -0
- data/Gemfile.lock +77 -0
- data/README.md +144 -57
- data/Rakefile +12 -43
- data/VERSION +1 -0
- data/couchrest_model.gemspec +35 -0
- data/history.txt +23 -1
- data/init.rb +1 -0
- data/lib/couchrest/model/associations.rb +17 -1
- data/lib/couchrest/model/base.rb +5 -5
- data/lib/couchrest/model/casted_model.rb +2 -2
- data/lib/couchrest/model/class_proxy.rb +6 -0
- data/lib/couchrest/model/collection.rb +3 -0
- data/lib/couchrest/model/configuration.rb +51 -0
- data/lib/couchrest/model/design_doc.rb +2 -5
- data/lib/couchrest/model/document_queries.rb +20 -3
- data/lib/couchrest/model/persistence.rb +15 -1
- data/lib/couchrest/model/properties.rb +97 -23
- data/lib/couchrest/model/property.rb +5 -4
- data/lib/couchrest/model/property_protection.rb +71 -0
- data/lib/couchrest/model/typecast.rb +12 -7
- data/lib/couchrest/model/view.rb +190 -0
- data/lib/couchrest/model/views.rb +3 -3
- data/lib/couchrest/model.rb +1 -1
- data/lib/couchrest/railtie.rb +3 -2
- data/lib/couchrest_model.rb +7 -14
- data/lib/rails/generators/couchrest_model/model/model_generator.rb +2 -1
- data/spec/.gitignore +1 -0
- data/spec/couchrest/base_spec.rb +3 -3
- data/spec/couchrest/casted_model_spec.rb +63 -49
- data/spec/couchrest/class_proxy_spec.rb +6 -0
- data/spec/couchrest/configuration_spec.rb +78 -0
- data/spec/couchrest/persistence_spec.rb +10 -4
- data/spec/couchrest/{attribute_protection_spec.rb → property_protection_spec.rb} +29 -2
- data/spec/couchrest/property_spec.rb +61 -0
- data/spec/couchrest/subclass_spec.rb +2 -2
- data/spec/couchrest/view_spec.rb +6 -0
- data/spec/fixtures/more/article.rb +1 -1
- data/spec/spec_helper.rb +4 -3
- metadata +96 -32
- data/lib/couchrest/model/attribute_protection.rb +0 -74
- data/lib/couchrest/model/attributes.rb +0 -75
data/lib/couchrest/model/base.rb
CHANGED
@@ -4,6 +4,7 @@ module CouchRest
|
|
4
4
|
|
5
5
|
extend ActiveModel::Naming
|
6
6
|
|
7
|
+
include CouchRest::Model::Configuration
|
7
8
|
include CouchRest::Model::Persistence
|
8
9
|
include CouchRest::Model::Callbacks
|
9
10
|
include CouchRest::Model::DocumentQueries
|
@@ -12,8 +13,7 @@ module CouchRest
|
|
12
13
|
include CouchRest::Model::ExtendedAttachments
|
13
14
|
include CouchRest::Model::ClassProxy
|
14
15
|
include CouchRest::Model::Collection
|
15
|
-
include CouchRest::Model::
|
16
|
-
include CouchRest::Model::Attributes
|
16
|
+
include CouchRest::Model::PropertyProtection
|
17
17
|
include CouchRest::Model::Associations
|
18
18
|
include CouchRest::Model::Validations
|
19
19
|
|
@@ -37,7 +37,7 @@ module CouchRest
|
|
37
37
|
|
38
38
|
# Accessors
|
39
39
|
attr_accessor :casted_by
|
40
|
-
|
40
|
+
|
41
41
|
|
42
42
|
# Instantiate a new CouchRest::Model::Base by preparing all properties
|
43
43
|
# using the provided document hash.
|
@@ -47,10 +47,10 @@ module CouchRest
|
|
47
47
|
# * :directly_set_attributes: true when data comes directly from database
|
48
48
|
#
|
49
49
|
def initialize(doc = {}, options = {})
|
50
|
-
prepare_all_attributes(doc, options)
|
50
|
+
doc = prepare_all_attributes(doc, options)
|
51
51
|
super(doc)
|
52
52
|
unless self['_id'] && self['_rev']
|
53
|
-
self[
|
53
|
+
self[self.model_type_key] = self.class.to_s
|
54
54
|
end
|
55
55
|
after_initialize if respond_to?(:after_initialize)
|
56
56
|
end
|
@@ -4,10 +4,10 @@ module CouchRest::Model
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
6
|
included do
|
7
|
-
include CouchRest::Model::
|
8
|
-
include CouchRest::Model::Attributes
|
7
|
+
include CouchRest::Model::Configuration
|
9
8
|
include CouchRest::Model::Callbacks
|
10
9
|
include CouchRest::Model::Properties
|
10
|
+
include CouchRest::Model::PropertyProtection
|
11
11
|
include CouchRest::Model::Associations
|
12
12
|
include CouchRest::Model::Validations
|
13
13
|
attr_accessor :casted_by
|
@@ -75,6 +75,12 @@ module CouchRest
|
|
75
75
|
doc
|
76
76
|
end
|
77
77
|
|
78
|
+
def last(opts = {})
|
79
|
+
doc = @klass.last({:database => @database}.merge(opts))
|
80
|
+
doc.database = @database if doc && doc.respond_to?(:database)
|
81
|
+
doc
|
82
|
+
end
|
83
|
+
|
78
84
|
def get(id)
|
79
85
|
doc = @klass.get(id, @database)
|
80
86
|
doc.database = @database if doc && doc.respond_to?(:database)
|
@@ -82,6 +82,7 @@ module CouchRest
|
|
82
82
|
design_doc['views'][view_name.to_s] &&
|
83
83
|
design_doc['views'][view_name.to_s]["couchrest-defaults"]) || {}
|
84
84
|
view_options = default_view_options.merge(options)
|
85
|
+
view_options.delete(:database)
|
85
86
|
|
86
87
|
[design_doc, view_name, view_options]
|
87
88
|
end
|
@@ -94,6 +95,8 @@ module CouchRest
|
|
94
95
|
raise ArgumentError, 'search_name is required' if search_name.nil?
|
95
96
|
|
96
97
|
search_options = options.clone
|
98
|
+
search_options.delete(:database)
|
99
|
+
|
97
100
|
[design_doc, search_name, search_options]
|
98
101
|
end
|
99
102
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module CouchRest
|
2
|
+
|
3
|
+
# CouchRest Model Configuration support, stolen from Carrierwave by jnicklas
|
4
|
+
# http://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/uploader/configuration.rb
|
5
|
+
|
6
|
+
module Model
|
7
|
+
module Configuration
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
add_config :model_type_key
|
12
|
+
add_config :mass_assign_any_attribute
|
13
|
+
|
14
|
+
configure do |config|
|
15
|
+
config.model_type_key = 'couchrest-type' # 'model'?
|
16
|
+
config.mass_assign_any_attribute = false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
|
22
|
+
def add_config(name)
|
23
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
24
|
+
def self.#{name}(value=nil)
|
25
|
+
@#{name} = value if value
|
26
|
+
return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
|
27
|
+
name = superclass.#{name}
|
28
|
+
return nil if name.nil? && !instance_variable_defined?("@#{name}")
|
29
|
+
@#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.#{name}=(value)
|
33
|
+
@#{name} = value
|
34
|
+
end
|
35
|
+
|
36
|
+
def #{name}
|
37
|
+
self.class.#{name}
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
end
|
41
|
+
|
42
|
+
def configure
|
43
|
+
yield self
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
@@ -2,10 +2,7 @@
|
|
2
2
|
module CouchRest
|
3
3
|
module Model
|
4
4
|
module DesignDoc
|
5
|
-
|
6
|
-
def self.included(base)
|
7
|
-
base.extend(ClassMethods)
|
8
|
-
end
|
5
|
+
extend ActiveSupport::Concern
|
9
6
|
|
10
7
|
module ClassMethods
|
11
8
|
|
@@ -34,7 +31,7 @@ module CouchRest
|
|
34
31
|
"views" => {
|
35
32
|
'all' => {
|
36
33
|
'map' => "function(doc) {
|
37
|
-
if (doc['
|
34
|
+
if (doc['#{self.model_type_key}'] == '#{self.to_s}') {
|
38
35
|
emit(doc['_id'],1);
|
39
36
|
}
|
40
37
|
}"
|
@@ -8,21 +8,21 @@ module CouchRest
|
|
8
8
|
|
9
9
|
module ClassMethods
|
10
10
|
|
11
|
-
# Load all documents that have the
|
11
|
+
# Load all documents that have the model_type_key's field equal to the
|
12
12
|
# name of the current class. Take the standard set of
|
13
13
|
# CouchRest::Database#view options.
|
14
14
|
def all(opts = {}, &block)
|
15
15
|
view(:all, opts, &block)
|
16
16
|
end
|
17
17
|
|
18
|
-
# Returns the number of documents that have the
|
18
|
+
# Returns the number of documents that have the model_type_key's field
|
19
19
|
# equal to the name of the current class. Takes the standard set of
|
20
20
|
# CouchRest::Database#view options
|
21
21
|
def count(opts = {}, &block)
|
22
22
|
all({:raw => true, :limit => 0}.merge(opts), &block)['total_rows']
|
23
23
|
end
|
24
24
|
|
25
|
-
# Load the first document that have the
|
25
|
+
# Load the first document that have the model_type_key's field equal to
|
26
26
|
# the name of the current class.
|
27
27
|
#
|
28
28
|
# ==== Returns
|
@@ -38,6 +38,22 @@ module CouchRest
|
|
38
38
|
first_instance.empty? ? nil : first_instance.first
|
39
39
|
end
|
40
40
|
|
41
|
+
# Load the last document that have the model_type_key's field equal to
|
42
|
+
# the name of the current class.
|
43
|
+
# It's similar to method first, just adds :descending => true
|
44
|
+
#
|
45
|
+
# ==== Returns
|
46
|
+
# Object:: The last object instance available
|
47
|
+
# or
|
48
|
+
# Nil:: if no instances available
|
49
|
+
#
|
50
|
+
# ==== Parameters
|
51
|
+
# opts<Hash>::
|
52
|
+
# View options, see <tt>CouchRest::Database#view</tt> options for more info.
|
53
|
+
def last(opts = {})
|
54
|
+
first(opts.merge!(:descending => true))
|
55
|
+
end
|
56
|
+
|
41
57
|
# Load a document from the database by id
|
42
58
|
# No exceptions will be raised if the document isn't found
|
43
59
|
#
|
@@ -70,6 +86,7 @@ module CouchRest
|
|
70
86
|
# id<String, Integer>:: Document ID
|
71
87
|
# db<Database>:: optional option to pass a custom database to use
|
72
88
|
def get!(id, db = database)
|
89
|
+
raise "Missing or empty document ID" if id.to_s.empty?
|
73
90
|
doc = db.get id
|
74
91
|
create_from_database(doc)
|
75
92
|
end
|
@@ -62,6 +62,20 @@ module CouchRest
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
# Update the document's attributes and save. For example:
|
66
|
+
#
|
67
|
+
# doc.update_attributes :name => "Fred"
|
68
|
+
#
|
69
|
+
# Is the equivilent of doing the following:
|
70
|
+
#
|
71
|
+
# doc.attributes = { :name => "Fred" }
|
72
|
+
# doc.save
|
73
|
+
#
|
74
|
+
def update_attributes(hash)
|
75
|
+
update_attributes_without_saving hash
|
76
|
+
save
|
77
|
+
end
|
78
|
+
|
65
79
|
protected
|
66
80
|
|
67
81
|
def perform_validations(options = {})
|
@@ -83,7 +97,7 @@ module CouchRest
|
|
83
97
|
# ==== Returns
|
84
98
|
# a document instance
|
85
99
|
def create_from_database(doc = {})
|
86
|
-
base = (doc[
|
100
|
+
base = (doc[model_type_key].blank? || doc[model_type_key] == self.to_s) ? self : doc[model_type_key].constantize
|
87
101
|
base.new(doc, :directly_set_attributes => true)
|
88
102
|
end
|
89
103
|
|
@@ -2,16 +2,12 @@
|
|
2
2
|
module CouchRest
|
3
3
|
module Model
|
4
4
|
module Properties
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
self.properties ||= []
|
12
|
-
EOS
|
13
|
-
base.extend(ClassMethods)
|
14
|
-
raise CouchRest::Mixins::Properties::IncludeError, "You can only mixin Properties in a class responding to [] and []=, if you tried to mixin CastedModel, make sure your class inherits from Hash or responds to the proper methods" unless (base.new.respond_to?(:[]) && base.new.respond_to?(:[]=))
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
extlib_inheritable_accessor(:properties) unless self.respond_to?(:properties)
|
9
|
+
self.properties ||= []
|
10
|
+
raise "You can only mixin Properties in a class responding to [] and []=, if you tried to mixin CastedModel, make sure your class inherits from Hash or responds to the proper methods" unless (method_defined?(:[]) && method_defined?(:[]=))
|
15
11
|
end
|
16
12
|
|
17
13
|
# Returns the Class properties
|
@@ -22,16 +18,45 @@ module CouchRest
|
|
22
18
|
self.class.properties
|
23
19
|
end
|
24
20
|
|
21
|
+
# Returns the Class properties with their values
|
22
|
+
#
|
23
|
+
# ==== Returns
|
24
|
+
# Array:: the list of properties with their values
|
25
|
+
def properties_with_values
|
26
|
+
props = {}
|
27
|
+
properties.each { |property| props[property.name] = read_attribute(property.name) }
|
28
|
+
props
|
29
|
+
end
|
30
|
+
|
31
|
+
# Read the casted value of an attribute defined with a property.
|
32
|
+
#
|
33
|
+
# ==== Returns
|
34
|
+
# Object:: the casted attibutes value.
|
25
35
|
def read_attribute(property)
|
26
|
-
self[property.to_s]
|
36
|
+
self[find_property!(property).to_s]
|
27
37
|
end
|
28
38
|
|
39
|
+
# Store a casted value in the current instance of an attribute defined
|
40
|
+
# with a property.
|
29
41
|
def write_attribute(property, value)
|
30
|
-
prop = property
|
31
|
-
|
32
|
-
|
42
|
+
prop = find_property!(property)
|
43
|
+
self[prop.to_s] = prop.is_a?(String) ? value : prop.cast(self, value)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Takes a hash as argument, and applies the values by using writer methods
|
47
|
+
# for each key. It doesn't save the document at the end. Raises a NoMethodError if the corresponding methods are
|
48
|
+
# missing. In case of error, no attributes are changed.
|
49
|
+
def update_attributes_without_saving(hash)
|
50
|
+
# Remove any protected and update all the rest. Any attributes
|
51
|
+
# which do not have a property will simply be ignored.
|
52
|
+
attrs = remove_protected_attributes(hash)
|
53
|
+
directly_set_attributes(attrs)
|
33
54
|
end
|
55
|
+
alias :attributes= :update_attributes_without_saving
|
34
56
|
|
57
|
+
|
58
|
+
private
|
59
|
+
# The following methods should be accessable by the Model::Base Class, but not by anything else!
|
35
60
|
def apply_all_property_defaults
|
36
61
|
return if self.respond_to?(:new?) && (new? == false)
|
37
62
|
# TODO: cache the default object
|
@@ -39,9 +64,57 @@ module CouchRest
|
|
39
64
|
write_attribute(property, property.default_value)
|
40
65
|
end
|
41
66
|
end
|
42
|
-
|
67
|
+
|
68
|
+
def prepare_all_attributes(doc = {}, options = {})
|
69
|
+
apply_all_property_defaults
|
70
|
+
if options[:directly_set_attributes]
|
71
|
+
directly_set_read_only_attributes(doc)
|
72
|
+
else
|
73
|
+
doc = remove_protected_attributes(doc)
|
74
|
+
end
|
75
|
+
directly_set_attributes(doc) unless doc.nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_property!(property)
|
79
|
+
prop = property.is_a?(Property) ? property : self.class.properties.detect {|p| p.to_s == property.to_s}
|
80
|
+
raise ArgumentError, "Missing property definition for #{property.to_s}" if prop.nil?
|
81
|
+
prop
|
82
|
+
end
|
83
|
+
|
84
|
+
# Set all the attributes and return a hash with the attributes
|
85
|
+
# that have not been accepted.
|
86
|
+
def directly_set_attributes(hash)
|
87
|
+
hash.reject do |attribute_name, attribute_value|
|
88
|
+
if self.respond_to?("#{attribute_name}=")
|
89
|
+
self.send("#{attribute_name}=", attribute_value)
|
90
|
+
true
|
91
|
+
elsif mass_assign_any_attribute # config option
|
92
|
+
self[attribute_name] = attribute_value
|
93
|
+
true
|
94
|
+
else
|
95
|
+
false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def directly_set_read_only_attributes(hash)
|
101
|
+
property_list = self.properties.map{|p| p.name}
|
102
|
+
hash.each do |attribute_name, attribute_value|
|
103
|
+
next if self.respond_to?("#{attribute_name}=")
|
104
|
+
if property_list.include?(attribute_name)
|
105
|
+
write_attribute(attribute_name, hash.delete(attribute_name))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def set_attributes(hash)
|
111
|
+
attrs = remove_protected_attributes(hash)
|
112
|
+
directly_set_attributes(attrs)
|
113
|
+
end
|
114
|
+
|
115
|
+
|
43
116
|
module ClassMethods
|
44
|
-
|
117
|
+
|
45
118
|
def property(name, *options, &block)
|
46
119
|
opts = { }
|
47
120
|
type = options.shift
|
@@ -64,16 +137,16 @@ module CouchRest
|
|
64
137
|
class_eval <<-EOS, __FILE__, __LINE__
|
65
138
|
property(:updated_at, Time, :read_only => true, :protected => true, :auto_validation => false)
|
66
139
|
property(:created_at, Time, :read_only => true, :protected => true, :auto_validation => false)
|
67
|
-
|
140
|
+
|
68
141
|
set_callback :save, :before do |object|
|
69
142
|
write_attribute('updated_at', Time.now)
|
70
143
|
write_attribute('created_at', Time.now) if object.new?
|
71
144
|
end
|
72
145
|
EOS
|
73
146
|
end
|
74
|
-
|
147
|
+
|
75
148
|
protected
|
76
|
-
|
149
|
+
|
77
150
|
# This is not a thread safe operation, if you have to set new properties at runtime
|
78
151
|
# make sure a mutex is used.
|
79
152
|
def define_property(name, options={}, &block)
|
@@ -87,7 +160,7 @@ module CouchRest
|
|
87
160
|
type = [type] # inject as an array
|
88
161
|
end
|
89
162
|
property = Property.new(name, type, options)
|
90
|
-
create_property_getter(property)
|
163
|
+
create_property_getter(property)
|
91
164
|
create_property_setter(property) unless property.read_only == true
|
92
165
|
if property.type_class.respond_to?(:validates_casted_model)
|
93
166
|
validates_casted_model property.name
|
@@ -95,7 +168,7 @@ module CouchRest
|
|
95
168
|
properties << property
|
96
169
|
property
|
97
170
|
end
|
98
|
-
|
171
|
+
|
99
172
|
# defines the getter for the property (and optional aliases)
|
100
173
|
def create_property_getter(property)
|
101
174
|
# meth = property.name
|
@@ -136,9 +209,10 @@ module CouchRest
|
|
136
209
|
EOS
|
137
210
|
end
|
138
211
|
end
|
139
|
-
|
212
|
+
|
140
213
|
end # module ClassMethods
|
141
|
-
|
214
|
+
|
142
215
|
end
|
143
216
|
end
|
144
217
|
end
|
218
|
+
|
@@ -33,7 +33,7 @@ module CouchRest::Model
|
|
33
33
|
data.keys.sort.each do |k|
|
34
34
|
value << data[k]
|
35
35
|
end
|
36
|
-
elsif value.
|
36
|
+
elsif !value.is_a?(Array)
|
37
37
|
raise "Expecting an array or keyed hash for property #{parent.class.name}##{self.name}"
|
38
38
|
end
|
39
39
|
arr = value.collect { |data| cast_value(parent, data) }
|
@@ -58,7 +58,8 @@ module CouchRest::Model
|
|
58
58
|
if default.class == Proc
|
59
59
|
default.call
|
60
60
|
else
|
61
|
-
Marshal.load(Marshal.dump(default))
|
61
|
+
# Marshal.load(Marshal.dump(default)) # Removed as there are no failing tests and caused mutex errors
|
62
|
+
default
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
@@ -72,12 +73,12 @@ module CouchRest::Model
|
|
72
73
|
def parse_type(type)
|
73
74
|
if type.nil?
|
74
75
|
@casted = false
|
75
|
-
@type = nil
|
76
|
+
@type = nil
|
76
77
|
@type_class = nil
|
77
78
|
else
|
78
79
|
base = type.is_a?(Array) ? type.first : type
|
79
80
|
base = Object if base.nil?
|
80
|
-
raise "Defining a property type as a #{type.class.name.humanize} is not supported in CouchRest Model!" if base.class != Class
|
81
|
+
raise "Defining a property type as a #{type.class.name.humanize} is not supported in CouchRest Model!" if base.class != Class
|
81
82
|
@type_class = base
|
82
83
|
@type = type
|
83
84
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module CouchRest
|
2
|
+
module Model
|
3
|
+
module PropertyProtection
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
# Property protection from mass assignment to CouchRest::Model properties
|
7
|
+
#
|
8
|
+
# Protected methods will be removed from
|
9
|
+
# * new
|
10
|
+
# * update_attributes
|
11
|
+
# * upate_attributes_without_saving
|
12
|
+
# * attributes=
|
13
|
+
#
|
14
|
+
# There are two modes of protection
|
15
|
+
# 1) Declare accessible poperties, and assume all unspecified properties are protected
|
16
|
+
# property :name, :accessible => true
|
17
|
+
# property :admin # this will be automatically protected
|
18
|
+
#
|
19
|
+
# 2) Declare protected properties, and assume all unspecified properties are accessible
|
20
|
+
# property :name # this will not be protected
|
21
|
+
# property :admin, :protected => true
|
22
|
+
#
|
23
|
+
# 3) Mix and match, and assume all unspecified properties are protected.
|
24
|
+
# property :name, :accessible => true
|
25
|
+
# property :admin, :protected => true # ignored
|
26
|
+
# property :phone # this will be automatically protected
|
27
|
+
#
|
28
|
+
# Note: the timestamps! method protectes the created_at and updated_at properties
|
29
|
+
|
30
|
+
|
31
|
+
def self.included(base)
|
32
|
+
base.extend(ClassMethods)
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
def accessible_properties
|
37
|
+
props = properties.select { |prop| prop.options[:accessible] }
|
38
|
+
if props.empty?
|
39
|
+
props = properties.select { |prop| !prop.options[:protected] }
|
40
|
+
end
|
41
|
+
props
|
42
|
+
end
|
43
|
+
|
44
|
+
def protected_properties
|
45
|
+
accessibles = accessible_properties
|
46
|
+
properties.reject { |prop| accessibles.include?(prop) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def accessible_properties
|
51
|
+
self.class.accessible_properties
|
52
|
+
end
|
53
|
+
|
54
|
+
def protected_properties
|
55
|
+
self.class.protected_properties
|
56
|
+
end
|
57
|
+
|
58
|
+
# Return a new copy of the attributes hash with protected attributes
|
59
|
+
# removed.
|
60
|
+
def remove_protected_attributes(attributes)
|
61
|
+
protected_names = protected_properties.map { |prop| prop.name }
|
62
|
+
return attributes if protected_names.empty? or attributes.nil?
|
63
|
+
|
64
|
+
attributes.reject do |property_name, property_value|
|
65
|
+
protected_names.include?(property_name.to_s)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,19 +1,23 @@
|
|
1
1
|
class Time
|
2
2
|
# returns a local time value much faster than Time.parse
|
3
3
|
def self.mktime_with_offset(string)
|
4
|
-
string =~ /(\d{4})[\-|\/](\d{2})[\-|\/](\d{2})[T|\s](\d{2}):(\d{2}):(\d{2})([\+|\s|\-])*(\d{2}):?(\d{2})
|
4
|
+
string =~ /(\d{4})[\-|\/](\d{2})[\-|\/](\d{2})[T|\s](\d{2}):(\d{2}):(\d{2})(([\+|\s|\-])*(\d{2}):?(\d{2}))?/
|
5
5
|
# $1 = year
|
6
6
|
# $2 = month
|
7
7
|
# $3 = day
|
8
8
|
# $4 = hours
|
9
9
|
# $5 = minutes
|
10
10
|
# $6 = seconds
|
11
|
-
# $
|
12
|
-
# $
|
11
|
+
# $8 = time zone direction
|
12
|
+
# $9 = tz difference
|
13
13
|
# utc time with wrong TZ info:
|
14
|
-
time = mktime($1, RFC2822_MONTH_NAME[$2.to_i - 1], $3, $4, $5, $6
|
15
|
-
|
16
|
-
|
14
|
+
time = mktime($1, RFC2822_MONTH_NAME[$2.to_i - 1], $3, $4, $5, $6)
|
15
|
+
if ($7)
|
16
|
+
tz_difference = ("#{$8 == '-' ? '+' : '-'}#{$9}".to_i * 3600)
|
17
|
+
time + tz_difference + zone_offset(time.zone)
|
18
|
+
else
|
19
|
+
time
|
20
|
+
end
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
@@ -75,7 +79,7 @@ module CouchRest
|
|
75
79
|
# Match numeric string
|
76
80
|
def typecast_to_numeric(value, method)
|
77
81
|
if value.respond_to?(:to_str)
|
78
|
-
if value.to_str =~ /\A(-?(?:0|[1-9]\d*)(?:\.\d+)?|(?:\.\d+))\z/
|
82
|
+
if value.gsub(/,/, '.').gsub(/\.(?!\d*\Z)/, '').to_str =~ /\A(-?(?:0|[1-9]\d*)(?:\.\d+)?|(?:\.\d+))\z/
|
79
83
|
$1.send(method)
|
80
84
|
else
|
81
85
|
value
|
@@ -128,6 +132,7 @@ module CouchRest
|
|
128
132
|
rescue ArgumentError
|
129
133
|
value
|
130
134
|
rescue TypeError
|
135
|
+
# After failures, resort to normal time parse
|
131
136
|
value
|
132
137
|
end
|
133
138
|
|