couchrest_model 1.0.0.beta8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|