couchrest_model 2.0.0.beta2 → 2.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/.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
|
+
|