couchrest_model 2.0.0.beta2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +8 -0
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/Rakefile +9 -24
- data/VERSION +1 -1
- data/couchrest_model.gemspec +7 -5
- data/history.md +17 -1
- data/lib/couchrest/model/associations.rb +16 -11
- data/lib/couchrest/model/base.rb +17 -15
- data/lib/couchrest/model/casted_array.rb +7 -1
- data/lib/couchrest/model/core_extensions/time_parsing.rb +0 -23
- data/lib/couchrest/model/design.rb +282 -0
- data/lib/couchrest/model/designs/design_mapper.rb +79 -0
- data/lib/couchrest/model/designs/view.rb +9 -6
- data/lib/couchrest/model/designs.rb +37 -70
- data/lib/couchrest/model/persistence.rb +5 -5
- data/lib/couchrest/model/properties.rb +5 -16
- data/lib/couchrest/model/property.rb +34 -16
- data/lib/couchrest/model/translation.rb +22 -0
- data/lib/couchrest/model/typecast.rb +54 -43
- data/lib/couchrest/model/utils/migrate.rb +106 -0
- data/lib/couchrest_model.rb +4 -2
- data/lib/tasks/migrations.rake +5 -5
- data/spec/fixtures/models/course.rb +1 -0
- data/spec/fixtures/models/designs.rb +22 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/assocations_spec.rb +7 -0
- data/spec/unit/base_spec.rb +3 -1
- data/spec/unit/{designs/design_spec.rb → design_spec.rb} +6 -6
- data/spec/unit/designs/design_mapper_spec.rb +124 -0
- data/spec/unit/designs/view_spec.rb +30 -4
- data/spec/unit/designs_spec.rb +5 -140
- data/spec/unit/dirty_spec.rb +15 -1
- data/spec/unit/embeddable_spec.rb +2 -2
- data/spec/unit/property_spec.rb +70 -28
- data/spec/unit/translations_spec.rb +31 -0
- data/spec/unit/typecast_spec.rb +99 -19
- data/spec/unit/utils/migrate_spec.rb +25 -0
- metadata +43 -19
- data/lib/couchrest/model/designs/design.rb +0 -284
- data/lib/couchrest/model/migrate.rb +0 -92
@@ -22,18 +22,37 @@ module CouchRest
|
|
22
22
|
|
23
23
|
module ClassMethods
|
24
24
|
|
25
|
-
#
|
26
|
-
#
|
27
|
-
|
25
|
+
# Define a Design Document associated with the current model.
|
26
|
+
#
|
27
|
+
# This class method supports several cool features that make it much
|
28
|
+
# easier to define design documents.
|
29
|
+
#
|
30
|
+
# Adding a prefix allows you to associate multiple design documents with the same
|
31
|
+
# model. This is useful if you'd like to split your designs into seperate
|
32
|
+
# use cases; one for regular search functions and a second for stats for example.
|
33
|
+
#
|
34
|
+
# # Create a design doc with id _design/Cats
|
35
|
+
# design do
|
36
|
+
# view :by_name
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# # Create a design doc with id _design/Cats_stats
|
40
|
+
# design :stats do
|
41
|
+
# view :by_age, :reduce => :stats
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
#
|
45
|
+
def design(*args, &block)
|
46
|
+
opts = prepare_design_options(*args)
|
28
47
|
|
29
48
|
# Store ourselves a copy of this design spec incase any other model inherits.
|
30
|
-
(@_design_blocks ||= [ ]) << {:args =>
|
49
|
+
(@_design_blocks ||= [ ]) << {:args => args, :block => block}
|
31
50
|
|
32
|
-
mapper = DesignMapper.new(self, prefix)
|
51
|
+
mapper = DesignMapper.new(self, opts[:prefix])
|
33
52
|
mapper.instance_eval(&block) if block_given?
|
34
53
|
|
35
54
|
# Create an 'all' view if no prefix and one has not been defined already
|
36
|
-
mapper.view(:all) if prefix.nil? and !mapper.design_doc.has_view?(:all)
|
55
|
+
mapper.view(:all) if opts[:prefix].nil? and !mapper.design_doc.has_view?(:all)
|
37
56
|
end
|
38
57
|
|
39
58
|
def inherited(model)
|
@@ -68,76 +87,24 @@ module CouchRest
|
|
68
87
|
@_design_docs ||= []
|
69
88
|
end
|
70
89
|
|
71
|
-
|
72
|
-
|
73
|
-
# Map method calls defined in a design block to actions
|
74
|
-
# in the Design Document.
|
75
|
-
class DesignMapper
|
76
|
-
|
77
|
-
# Basic mapper attributes
|
78
|
-
attr_accessor :model, :method, :prefix
|
79
|
-
|
80
|
-
# Temporary variable storing the design doc
|
81
|
-
attr_accessor :design_doc
|
82
|
-
|
83
|
-
def initialize(model, prefix = nil)
|
84
|
-
self.model = model
|
85
|
-
self.prefix = prefix
|
86
|
-
self.method = Design.method_name(prefix)
|
87
|
-
|
88
|
-
create_model_design_doc_reader
|
89
|
-
self.design_doc = model.send(method) || assign_model_design_doc
|
90
|
-
end
|
91
|
-
|
92
|
-
def disable_auto_update
|
93
|
-
design_doc.auto_update = false
|
94
|
-
end
|
95
|
-
|
96
|
-
def enable_auto_update
|
97
|
-
design_doc.auto_update = true
|
98
|
-
end
|
99
|
-
|
100
|
-
# Add the specified view to the design doc the definition was made in
|
101
|
-
# and create quick access methods in the model.
|
102
|
-
def view(name, opts = {})
|
103
|
-
design_doc.create_view(name, opts)
|
104
|
-
end
|
105
|
-
|
106
|
-
# Really simple design function that allows a filter
|
107
|
-
# to be added. Filters are simple functions used when listening
|
108
|
-
# to the _changes feed.
|
109
|
-
#
|
110
|
-
# No methods are created here, the design is simply updated.
|
111
|
-
# See the CouchDB API for more information on how to use this.
|
112
|
-
def filter(name, function)
|
113
|
-
design_doc.create_filter(name, function)
|
114
|
-
end
|
115
|
-
|
116
|
-
# Convenience wrapper to access model's type key option.
|
117
|
-
def model_type_key
|
118
|
-
model.model_type_key
|
119
|
-
end
|
120
|
-
|
121
|
-
protected
|
90
|
+
private
|
122
91
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
92
|
+
def prepare_design_options(*args)
|
93
|
+
options = {}
|
94
|
+
if !args.first.is_a?(Hash)
|
95
|
+
options[:prefix] = args.shift
|
96
|
+
end
|
97
|
+
options.merge(args.last) unless args.empty?
|
98
|
+
prepare_source_paths(options)
|
99
|
+
options
|
127
100
|
end
|
128
101
|
|
129
|
-
def
|
130
|
-
|
131
|
-
model.instance_variable_set("@#{method}", doc)
|
132
|
-
model.design_docs << doc
|
133
|
-
|
134
|
-
# Set defaults
|
135
|
-
doc.auto_update = model.auto_update_design_doc
|
136
|
-
|
137
|
-
doc
|
102
|
+
def prepare_source_paths(options)
|
103
|
+
|
138
104
|
end
|
139
105
|
|
140
106
|
end
|
107
|
+
|
141
108
|
end
|
142
109
|
end
|
143
110
|
end
|
@@ -8,8 +8,8 @@ module CouchRest
|
|
8
8
|
# be returned.
|
9
9
|
def create(options = {})
|
10
10
|
return false unless perform_validations(options)
|
11
|
-
|
12
|
-
|
11
|
+
run_callbacks :create do
|
12
|
+
run_callbacks :save do
|
13
13
|
set_unique_id if new? && self.respond_to?(:set_unique_id)
|
14
14
|
result = database.save_doc(self)
|
15
15
|
ret = (result["ok"] == true) ? self : false
|
@@ -32,8 +32,8 @@ module CouchRest
|
|
32
32
|
raise "Calling #{self.class.name}#update on document that has not been created!" if new?
|
33
33
|
return false unless perform_validations(options)
|
34
34
|
return true if !self.disable_dirty && !self.changed?
|
35
|
-
|
36
|
-
|
35
|
+
run_callbacks :update do
|
36
|
+
run_callbacks :save do
|
37
37
|
result = database.save_doc(self)
|
38
38
|
ret = result["ok"] == true
|
39
39
|
@changed_attributes.clear if ret && @changed_attributes
|
@@ -56,7 +56,7 @@ module CouchRest
|
|
56
56
|
|
57
57
|
# Deletes the document from the database. Runs the :destroy callbacks.
|
58
58
|
def destroy
|
59
|
-
|
59
|
+
run_callbacks :destroy do
|
60
60
|
result = database.delete_doc(self)
|
61
61
|
if result['ok']
|
62
62
|
@_destroyed = true
|
@@ -205,26 +205,15 @@ module CouchRest
|
|
205
205
|
|
206
206
|
# This is not a thread safe operation, if you have to set new properties at runtime
|
207
207
|
# make sure a mutex is used.
|
208
|
-
def define_property(name, options={}, &block)
|
209
|
-
|
210
|
-
type = options.delete(:type) || options.delete(:cast_as)
|
211
|
-
if block_given?
|
212
|
-
type = Class.new do
|
213
|
-
include Embeddable
|
214
|
-
end
|
215
|
-
if block.arity == 1 # Traditional, with options
|
216
|
-
type.class_eval { yield type }
|
217
|
-
else
|
218
|
-
type.instance_exec(&block)
|
219
|
-
end
|
220
|
-
type = [type] # inject as an array
|
221
|
-
end
|
222
|
-
property = Property.new(name, type, options)
|
208
|
+
def define_property(name, options = {}, &block)
|
209
|
+
property = Property.new(name, options, &block)
|
223
210
|
create_property_getter(property)
|
224
211
|
create_property_setter(property) unless property.read_only == true
|
225
|
-
|
212
|
+
|
213
|
+
if property.type.respond_to?(:validates_casted_model)
|
226
214
|
validates_casted_model property.name
|
227
215
|
end
|
216
|
+
|
228
217
|
properties << property
|
229
218
|
properties_by_name[property.to_s] = property
|
230
219
|
property
|
@@ -4,26 +4,29 @@ module CouchRest::Model
|
|
4
4
|
|
5
5
|
include ::CouchRest::Model::Typecast
|
6
6
|
|
7
|
-
attr_reader :name, :type, :
|
7
|
+
attr_reader :name, :type, :array, :read_only, :alias, :default, :casted, :init_method, :options, :allow_blank
|
8
8
|
|
9
9
|
# Attribute to define.
|
10
10
|
# All Properties are assumed casted unless the type is nil.
|
11
|
-
def initialize(name,
|
11
|
+
def initialize(name, options = {}, &block)
|
12
12
|
@name = name.to_s
|
13
|
-
@casted = true
|
14
|
-
parse_type(type)
|
15
13
|
parse_options(options)
|
14
|
+
parse_type(options, &block)
|
16
15
|
self
|
17
16
|
end
|
18
17
|
|
19
18
|
def to_s
|
20
19
|
name
|
21
20
|
end
|
21
|
+
|
22
|
+
def to_sym
|
23
|
+
@_sym_name ||= name.to_sym
|
24
|
+
end
|
22
25
|
|
23
26
|
# Cast the provided value using the properties details.
|
24
27
|
def cast(parent, value)
|
25
28
|
return value unless casted
|
26
|
-
if
|
29
|
+
if array
|
27
30
|
if value.nil?
|
28
31
|
value = []
|
29
32
|
elsif [Hash, HashWithIndifferentAccess].include?(value.class)
|
@@ -68,12 +71,12 @@ module CouchRest::Model
|
|
68
71
|
# used. If a proc is defined for the init method, it will be used instead of
|
69
72
|
# a normal call to the class.
|
70
73
|
def build(*args)
|
71
|
-
raise StandardError, "Cannot build property without a class" if @
|
74
|
+
raise StandardError, "Cannot build property without a class" if @type.nil?
|
72
75
|
|
73
76
|
if @init_method.is_a?(Proc)
|
74
77
|
@init_method.call(*args)
|
75
78
|
else
|
76
|
-
@
|
79
|
+
@type.send(@init_method, *args)
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
@@ -93,21 +96,23 @@ module CouchRest::Model
|
|
93
96
|
value
|
94
97
|
end
|
95
98
|
|
96
|
-
def parse_type(
|
97
|
-
if
|
99
|
+
def parse_type(options, &block)
|
100
|
+
set_type_from_block(&block) if block_given?
|
101
|
+
if @type.nil?
|
98
102
|
@casted = false
|
99
|
-
@type = nil
|
100
|
-
@type_class = nil
|
101
103
|
else
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
@casted = true
|
105
|
+
if @type.is_a?(Array)
|
106
|
+
@type = @type.first || Object
|
107
|
+
@array = true
|
108
|
+
end
|
109
|
+
raise "Defining a property type as a #{@type.class.name.humanize} is not supported in CouchRest Model!" if @type.class != Class
|
107
110
|
end
|
108
111
|
end
|
109
112
|
|
110
113
|
def parse_options(options)
|
114
|
+
@type = options.delete(:type) || options.delete(:cast_as)
|
115
|
+
@array = !!options.delete(:array)
|
111
116
|
@validation_format = options.delete(:format) if options[:format]
|
112
117
|
@read_only = options.delete(:read_only) if options[:read_only]
|
113
118
|
@alias = options.delete(:alias) if options[:alias]
|
@@ -116,5 +121,18 @@ module CouchRest::Model
|
|
116
121
|
@allow_blank = options[:allow_blank].nil? ? true : options.delete(:allow_blank)
|
117
122
|
@options = options
|
118
123
|
end
|
124
|
+
|
125
|
+
|
126
|
+
def set_type_from_block(&block)
|
127
|
+
@type = Class.new do
|
128
|
+
include Embeddable
|
129
|
+
end
|
130
|
+
if block.arity == 1 # Traditional, with options
|
131
|
+
@type.class_eval(&block)
|
132
|
+
else
|
133
|
+
@type.instance_eval(&block)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
119
137
|
end
|
120
138
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module CouchRest
|
2
|
+
module Model
|
3
|
+
module Translation
|
4
|
+
include ActiveModel::Translation
|
5
|
+
|
6
|
+
def lookup_ancestors #:nodoc:
|
7
|
+
klass = self
|
8
|
+
classes = [klass]
|
9
|
+
return classes if klass == CouchRest::Model::Base
|
10
|
+
|
11
|
+
while klass.superclass != CouchRest::Model::Base
|
12
|
+
classes << klass = klass.superclass
|
13
|
+
end
|
14
|
+
classes
|
15
|
+
end
|
16
|
+
|
17
|
+
def i18n_scope
|
18
|
+
:couchrest
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -4,17 +4,17 @@ module CouchRest
|
|
4
4
|
|
5
5
|
def typecast_value(parent, property, value)
|
6
6
|
return nil if value.nil?
|
7
|
-
|
8
|
-
if value.instance_of?(
|
9
|
-
if
|
7
|
+
type = property.type
|
8
|
+
if value.instance_of?(type) || type == Object
|
9
|
+
if type == Time && !value.utc?
|
10
10
|
value.utc # Ensure Time is always in UTC
|
11
11
|
else
|
12
12
|
value
|
13
13
|
end
|
14
|
-
elsif
|
15
|
-
|
16
|
-
elsif [String, TrueClass, Integer, Float, BigDecimal, DateTime, Time, Date, Class].include?(
|
17
|
-
send('typecast_to_'+
|
14
|
+
elsif type.respond_to?(:couchrest_typecast)
|
15
|
+
type.couchrest_typecast(parent, property, value)
|
16
|
+
elsif [String, Symbol, TrueClass, Integer, Float, BigDecimal, DateTime, Time, Date, Class].include?(type)
|
17
|
+
send('typecast_to_'+type.to_s.downcase, value)
|
18
18
|
else
|
19
19
|
property.build(value)
|
20
20
|
end
|
@@ -27,30 +27,9 @@ module CouchRest
|
|
27
27
|
typecast_to_numeric(value, :to_i)
|
28
28
|
end
|
29
29
|
|
30
|
-
# Typecast a value to a String
|
31
|
-
def typecast_to_string(value)
|
32
|
-
value.to_s
|
33
|
-
end
|
34
|
-
|
35
|
-
# Typecast a value to a true or false
|
36
|
-
def typecast_to_trueclass(value)
|
37
|
-
if value.kind_of?(Integer)
|
38
|
-
return true if value == 1
|
39
|
-
return false if value == 0
|
40
|
-
elsif value.respond_to?(:to_s)
|
41
|
-
return true if %w[ true 1 t ].include?(value.to_s.downcase)
|
42
|
-
return false if %w[ false 0 f ].include?(value.to_s.downcase)
|
43
|
-
end
|
44
|
-
value
|
45
|
-
end
|
46
|
-
|
47
30
|
# Typecast a value to a BigDecimal
|
48
31
|
def typecast_to_bigdecimal(value)
|
49
|
-
|
50
|
-
value.to_s.to_d
|
51
|
-
else
|
52
|
-
typecast_to_numeric(value, :to_d)
|
53
|
-
end
|
32
|
+
typecast_to_numeric(value, :to_d)
|
54
33
|
end
|
55
34
|
|
56
35
|
# Typecast a value to a Float
|
@@ -58,21 +37,50 @@ module CouchRest
|
|
58
37
|
typecast_to_numeric(value, :to_f)
|
59
38
|
end
|
60
39
|
|
61
|
-
#
|
40
|
+
# Convert some kind of object to a number that of the type
|
41
|
+
# provided.
|
42
|
+
#
|
43
|
+
# When a string is provided, It'll attempt to filter out
|
44
|
+
# region specific details such as commas instead of points
|
45
|
+
# for decimal places, text units, and anything else that is
|
46
|
+
# not a number and a human could make out.
|
47
|
+
#
|
48
|
+
# Esentially, the aim is to provide some kind of sanitary
|
49
|
+
# conversion from values in incoming http forms.
|
50
|
+
#
|
51
|
+
# If what we get makes no sense at all, nil it.
|
62
52
|
def typecast_to_numeric(value, method)
|
63
|
-
if value.
|
64
|
-
|
65
|
-
|
66
|
-
else
|
67
|
-
value
|
68
|
-
end
|
53
|
+
if value.is_a?(String)
|
54
|
+
value = value.strip.gsub(/,/, '.').gsub(/[^\d\-\.]/, '').gsub(/\.(?!\d*\Z)/, '')
|
55
|
+
value.empty? ? nil : value.send(method)
|
69
56
|
elsif value.respond_to?(method)
|
70
57
|
value.send(method)
|
71
58
|
else
|
72
|
-
|
59
|
+
nil
|
73
60
|
end
|
74
61
|
end
|
75
62
|
|
63
|
+
# Typecast a value to a String
|
64
|
+
def typecast_to_string(value)
|
65
|
+
value.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
def typecast_to_symbol(value)
|
69
|
+
value.to_sym
|
70
|
+
end
|
71
|
+
|
72
|
+
# Typecast a value to a true or false
|
73
|
+
def typecast_to_trueclass(value)
|
74
|
+
if value.kind_of?(Integer)
|
75
|
+
return true if value == 1
|
76
|
+
return false if value == 0
|
77
|
+
elsif value.respond_to?(:to_s)
|
78
|
+
return true if %w[ true 1 t ].include?(value.to_s.downcase)
|
79
|
+
return false if %w[ false 0 f ].include?(value.to_s.downcase)
|
80
|
+
end
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
76
84
|
# Typecasts an arbitrary value to a DateTime.
|
77
85
|
# Handles both Hashes and DateTime instances.
|
78
86
|
# This is slow!! Use Time instead.
|
@@ -83,7 +91,7 @@ module CouchRest
|
|
83
91
|
DateTime.parse(value.to_s)
|
84
92
|
end
|
85
93
|
rescue ArgumentError
|
86
|
-
|
94
|
+
nil
|
87
95
|
end
|
88
96
|
|
89
97
|
# Typecasts an arbitrary value to a Date
|
@@ -100,21 +108,24 @@ module CouchRest
|
|
100
108
|
Date.parse(value)
|
101
109
|
end
|
102
110
|
rescue ArgumentError
|
103
|
-
|
111
|
+
nil
|
104
112
|
end
|
105
113
|
|
106
114
|
# Typecasts an arbitrary value to a Time
|
107
115
|
# Handles both Hashes and Time instances.
|
108
116
|
def typecast_to_time(value)
|
109
|
-
|
117
|
+
case value
|
118
|
+
when Float # JSON oj already parses Time, FTW.
|
119
|
+
Time.at(value).utc
|
120
|
+
when Hash
|
110
121
|
typecast_hash_to_time(value)
|
111
122
|
else
|
112
123
|
Time.parse_iso8601(value.to_s)
|
113
124
|
end
|
114
125
|
rescue ArgumentError
|
115
|
-
|
126
|
+
nil
|
116
127
|
rescue TypeError
|
117
|
-
|
128
|
+
nil
|
118
129
|
end
|
119
130
|
|
120
131
|
# Creates a DateTime instance from a Hash with keys :year, :month, :day,
|
@@ -147,7 +158,7 @@ module CouchRest
|
|
147
158
|
def typecast_to_class(value)
|
148
159
|
value.to_s.constantize
|
149
160
|
rescue NameError
|
150
|
-
|
161
|
+
nil
|
151
162
|
end
|
152
163
|
|
153
164
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module CouchRest
|
2
|
+
module Model
|
3
|
+
module Utils
|
4
|
+
|
5
|
+
# Handle CouchDB Design Document migrations.
|
6
|
+
#
|
7
|
+
# Actual migrations are handled by the Design document, this serves as a utility
|
8
|
+
# to find all the CouchRest Model submodels and perform the migration on them.
|
9
|
+
#
|
10
|
+
# Also contains some more advanced support for handling proxied models.
|
11
|
+
#
|
12
|
+
# Examples of usage:
|
13
|
+
#
|
14
|
+
# # Ensure all models have been loaded (only Rails)
|
15
|
+
# CouchRest::Model::Utils::Migrate.load_all_models
|
16
|
+
#
|
17
|
+
# # Migrate all regular models (not proxied)
|
18
|
+
# CouchRest::Model::Utils::Migrate.all_models
|
19
|
+
#
|
20
|
+
# # Migrate all models and submodels of proxies
|
21
|
+
# CouchRest::Model::Utils::Migrate.all_models_and_proxies
|
22
|
+
#
|
23
|
+
# Typically however you'd want to run these methods from the rake tasks:
|
24
|
+
#
|
25
|
+
# $ rake couchrest:migrate_with_proxies
|
26
|
+
#
|
27
|
+
# NOTE: This is an experimental feature that is not yet properly tested.
|
28
|
+
#
|
29
|
+
module Migrate
|
30
|
+
extend self
|
31
|
+
|
32
|
+
# Make an attempt at loading all the files in this Rails application's
|
33
|
+
# models directory.
|
34
|
+
def load_all_models
|
35
|
+
# Make a reasonable effort to load all models
|
36
|
+
return unless defined?(Rails)
|
37
|
+
Dir[Rails.root + 'app/models/**/*.rb'].each do |path|
|
38
|
+
require path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Go through each class that inherits from CouchRest::Model::Base and
|
43
|
+
# attempt to migrate the design documents.
|
44
|
+
def all_models
|
45
|
+
callbacks = migrate_each_model(find_models)
|
46
|
+
cleanup(callbacks)
|
47
|
+
end
|
48
|
+
|
49
|
+
def all_models_and_proxies
|
50
|
+
callbacks = migrate_each_model(find_models)
|
51
|
+
callbacks += migrate_each_proxying_model(find_proxying_models)
|
52
|
+
cleanup(callbacks)
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def find_models
|
58
|
+
CouchRest::Model::Base.subclasses.reject{|m| m.proxy_owner_method.present?}
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_proxying_models
|
62
|
+
CouchRest::Model::Base.subclasses.reject{|m| m.proxy_database_method.blank?}
|
63
|
+
end
|
64
|
+
|
65
|
+
def migrate_each_model(models, db = nil)
|
66
|
+
callbacks = [ ]
|
67
|
+
models.each do |model|
|
68
|
+
model.design_docs.each do |design|
|
69
|
+
callbacks << migrate_design(model, design, db)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
callbacks
|
73
|
+
end
|
74
|
+
|
75
|
+
def migrate_each_proxying_model(models)
|
76
|
+
callbacks = [ ]
|
77
|
+
models.each do |model|
|
78
|
+
submodels = model.proxied_model_names.map{|n| n.constantize}
|
79
|
+
model.all.each do |base|
|
80
|
+
puts "Finding proxied models for #{model}: \"#{base.send(model.proxy_database_method)}\""
|
81
|
+
callbacks += migrate_each_model(submodels, base.proxy_database)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
callbacks
|
85
|
+
end
|
86
|
+
|
87
|
+
def migrate_design(model, design, db = nil)
|
88
|
+
print "Migrating #{model.to_s}##{design.method_name}... "
|
89
|
+
callback = design.migrate(db) do |result|
|
90
|
+
puts "#{result.to_s.gsub(/_/, ' ')}"
|
91
|
+
end
|
92
|
+
# Return the callback hash if there is one
|
93
|
+
callback ? {:design => design, :proc => callback, :db => db || model.database} : nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def cleanup(methods)
|
97
|
+
methods.compact.each do |cb|
|
98
|
+
name = "/#{cb[:db].name}/#{cb[:design]['_id']}"
|
99
|
+
puts "Activating new design: #{name}"
|
100
|
+
cb[:proc].call
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/couchrest_model.rb
CHANGED
@@ -24,6 +24,7 @@ require 'couchrest'
|
|
24
24
|
|
25
25
|
require 'couchrest/model'
|
26
26
|
require 'couchrest/model/errors'
|
27
|
+
require 'couchrest/model/translation'
|
27
28
|
require "couchrest/model/persistence"
|
28
29
|
require "couchrest/model/typecast"
|
29
30
|
require "couchrest/model/casted_by"
|
@@ -41,8 +42,9 @@ require "couchrest/model/proxyable"
|
|
41
42
|
require "couchrest/model/associations"
|
42
43
|
require "couchrest/model/configuration"
|
43
44
|
require "couchrest/model/connection"
|
45
|
+
require "couchrest/model/design"
|
44
46
|
require "couchrest/model/designs"
|
45
|
-
require "couchrest/model/designs/
|
47
|
+
require "couchrest/model/designs/design_mapper"
|
46
48
|
require "couchrest/model/designs/view"
|
47
49
|
|
48
50
|
# Monkey patches applied to couchrest
|
@@ -57,7 +59,7 @@ require "couchrest/model/embeddable"
|
|
57
59
|
require "couchrest/model/base"
|
58
60
|
|
59
61
|
# Design Migration support
|
60
|
-
require "couchrest/model/migrate.rb"
|
62
|
+
require "couchrest/model/utils/migrate.rb"
|
61
63
|
|
62
64
|
# Add rails support *after* everything has loaded
|
63
65
|
if defined?(Rails)
|
data/lib/tasks/migrations.rake
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
#
|
2
2
|
# CouchRest Migration Rake Tasks
|
3
3
|
#
|
4
|
-
#
|
4
|
+
# See the CouchRest::Model::Utils::Migrate class for more details.
|
5
5
|
#
|
6
6
|
namespace :couchrest do
|
7
7
|
|
8
8
|
desc "Migrate all the design docs found in each model"
|
9
9
|
task :migrate => :environment do
|
10
|
-
CouchRest::Model::Migrate.load_all_models
|
11
|
-
CouchRest::Model::Migrate.all_models
|
10
|
+
CouchRest::Model::Utils::Migrate.load_all_models
|
11
|
+
CouchRest::Model::Utils::Migrate.all_models
|
12
12
|
end
|
13
13
|
|
14
14
|
desc "Migrate all the design docs "
|
15
15
|
task :migrate_with_proxies => :environment do
|
16
|
-
CouchRest::Model::Migrate.load_all_models
|
17
|
-
CouchRest::Model::Migrate.all_models_and_proxies
|
16
|
+
CouchRest::Model::Utils::Migrate.load_all_models
|
17
|
+
CouchRest::Model::Utils::Migrate.all_models_and_proxies
|
18
18
|
end
|
19
19
|
|
20
20
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
class DesignModel < CouchRest::Model::Base
|
3
|
+
use_database DB
|
4
|
+
property :name
|
5
|
+
end
|
6
|
+
|
7
|
+
class DesignsModel < CouchRest::Model::Base
|
8
|
+
use_database DB
|
9
|
+
property :name
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
class DesignsNoAutoUpdate < CouchRest::Model::Base
|
14
|
+
use_database DB
|
15
|
+
property :title, String
|
16
|
+
design do
|
17
|
+
disable_auto_update
|
18
|
+
view :by_title_fail, :by => ['title']
|
19
|
+
view :by_title, :reduce => true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|