davber_couch_potato 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.md CHANGED
@@ -1,7 +1,24 @@
1
1
  ## Changes
2
2
 
3
- ### 0.3.0 - cloned gem
3
+ ### 0.4.0
4
+ * ruby 1.9.2 compatibility (langalex)
5
+ * couch potato objects now behave correctly when used as keys in Hashes (langalex)
6
+ * use as\_json instead of to\_s(:json), which is the rails way
7
+ * use ActiveModel dirty tracking (langalex) - this means no more "deep tracking", e.g. `user.tags << 'new_tag'; user.dirty? # false`
8
+
9
+ ### 0.3.2
10
+ * support yielding to blocks on #initialize (martinrehfeld)
11
+ * support for negative numbers in Fixnum/Float properties (langalex)
12
+
13
+ ### 0.3.1
14
+ * ActiveModel callbacks (kazjote)
15
+ * do not use Rails.env in initializer as it will free Rails.env for all times and in Rails 2.3.x apps it will be called too early thus always beeing development (jweiss)
16
+ * ruby 1.9.2 compatibility (langalex)
17
+ * can configure validation framework in couchdb.yml, process couchdb.yml with erb (langalex)
18
+
19
+ ### 0.3.0 - cloned gem!
4
20
  * type field of documents now configurable, by using CouchPotato.type_field (davber)
21
+ * the mapping from class to design document name is now configurable, by using CouchPotato.design_name_fun, which is supposed to be a Proc (davber)
5
22
 
6
23
  ### 0.3.0
7
24
  * support for lists (langalex)
data/README.md CHANGED
@@ -22,6 +22,13 @@ Lastly Couch Potato aims to provide a seamless integration with Ruby on Rails, e
22
22
  * declarative views with either custom or generated map/reduce functions
23
23
  * extensive spec suite
24
24
 
25
+ ### Supported Environments
26
+
27
+ * Ruby 1.8.7, 1.9.1, 1.9.2
28
+ * CouchDB 1.0.1
29
+
30
+ (Supported means I run the specs against those before releasing a new gem.)
31
+
25
32
  ### Installation
26
33
 
27
34
  Couch Potato is hosted as a gem which you can install like this:
@@ -51,18 +58,40 @@ Optionally you can configure which framework you want to use for validations (ei
51
58
 
52
59
  #### Using with Rails
53
60
 
54
- Add to your config/environment.rb:
61
+ Create a config/couchdb.yml:
62
+
63
+ default: &default
64
+ validation_framework: :active_model #optional
65
+
66
+ development:
67
+ <<: *default
68
+ database: development_db_name
69
+ test:
70
+ <<: *default
71
+ database: test_db_name
72
+ production:
73
+ <<: *default
74
+ database: <%= ENV['DB_NAME'] %>
75
+
76
+ #### Rails 2.x
77
+
78
+ Add to your _config/environment.rb_:
55
79
 
56
80
  config.gem 'davber_couch_potato', :source => 'http://gemcutter.org'
57
81
  config.frameworks -= [:active_record] # if you switch completely
82
+
83
+ #### Rails 3.x
58
84
 
59
- Then create a config/couchdb.yml:
85
+ Add to your _Gemfile_:
60
86
 
61
- development: development_db_name
62
- test: test_db_name
63
- production: http://db.server/production_db_name
87
+ # gem 'rails' # we don't want to load activerecord so we can't require rails
88
+ gem 'railties'
89
+ gem 'actionpack'
90
+ gem 'actionmailer'
91
+ gem 'activemodel'
92
+ gem "couch_potato"
64
93
 
65
- Alternatively you can also install Couch Potato directly as a plugin.
94
+ Note: please make sure that when you run `Date.today.as_json` in the Rails console it returns something like `2010/12/10` and not `2010-12-10` - if it does another gem has overwritten Couch Potato's Date patches - in this case move Couch Potato further down in your Gemfile or whereever you load it.
66
95
 
67
96
  ### Introduction
68
97
 
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 3
3
+ :minor: 4
4
4
  :patch: 0
5
5
  :build:
data/lib/core_ext/date.rb CHANGED
@@ -1,18 +1,11 @@
1
1
  class Date
2
2
  def to_json(*a)
3
- %("#{to_s(:json)}")
3
+ %("#{as_json}")
4
4
  end
5
5
 
6
- def to_s_with_json(*args)
7
- if args[0] == :json
8
- strftime("%Y/%m/%d")
9
- else
10
- to_s_without_json *args
11
- end
6
+ def as_json(*args)
7
+ strftime("%Y/%m/%d")
12
8
  end
13
- alias_method :to_s_without_json, :to_s
14
- alias_method :to_s, :to_s_with_json
15
-
16
9
 
17
10
  def self.json_create string
18
11
  return nil if string.nil?
@@ -4,5 +4,9 @@ module ActiveSupportMethods
4
4
  $1.upcase
5
5
  end
6
6
  end
7
+
8
+ def blank?
9
+ empty?
10
+ end
7
11
  end
8
- String.send :include, ActiveSupportMethods unless String.new.respond_to?(:underscore)
12
+ String.send :include, ActiveSupportMethods unless String.new.respond_to?(:underscore)
data/lib/core_ext/time.rb CHANGED
@@ -1,17 +1,11 @@
1
1
  class Time
2
2
  def to_json(*a)
3
- %("#{to_s(:json)}")
3
+ %("#{as_json}")
4
4
  end
5
5
 
6
- def to_s_with_json(*args)
7
- if args[0] == :json
8
- getutc.strftime("%Y/%m/%d %H:%M:%S +0000")
9
- else
10
- to_s_without_json *args
11
- end
6
+ def as_json(*args)
7
+ getutc.strftime("%Y/%m/%d %H:%M:%S +0000")
12
8
  end
13
- alias_method :to_s_without_json, :to_s
14
- alias_method :to_s, :to_s_with_json
15
9
 
16
10
  def self.json_create string
17
11
  return nil if string.nil?
@@ -63,7 +63,7 @@ module CouchPotato
63
63
 
64
64
  # saves a document. returns true on success, false on failure
65
65
  def save_document(document, validate = true)
66
- return true unless document.dirty?
66
+ return true unless document.dirty? || document.new?
67
67
  if document.new?
68
68
  create_document(document, validate)
69
69
  else
@@ -79,10 +79,10 @@ module CouchPotato
79
79
  alias_method :save!, :save_document!
80
80
 
81
81
  def destroy_document(document)
82
- document.run_callbacks :before_destroy
83
- document._deleted = true
84
- database.delete_doc document.to_hash
85
- document.run_callbacks :after_destroy
82
+ document.run_callbacks :destroy do
83
+ document._deleted = true
84
+ database.delete_doc document.to_hash
85
+ end
86
86
  document._id = nil
87
87
  document._rev = nil
88
88
  end
@@ -102,7 +102,7 @@ module CouchPotato
102
102
  alias_method :load, :load_document
103
103
 
104
104
  def inspect #:nodoc:
105
- "#<CouchPotato::Database>"
105
+ "#<CouchPotato::Database @root=\"#{database.root}\">"
106
106
  end
107
107
 
108
108
  private
@@ -112,35 +112,39 @@ module CouchPotato
112
112
 
113
113
  if validate
114
114
  document.errors.clear
115
- document.run_callbacks :before_validation_on_save
116
- document.run_callbacks :before_validation_on_create
117
- return false unless valid_document?(document)
115
+ document.run_callbacks :validation_on_save do
116
+ document.run_callbacks :validation_on_create do
117
+ return false unless valid_document?(document)
118
+ end
119
+ end
118
120
  end
119
121
 
120
- document.run_callbacks :before_save
121
- document.run_callbacks :before_create
122
- res = database.save_doc document.to_hash
123
- document._rev = res['rev']
124
- document._id = res['id']
125
- document.run_callbacks :after_save
126
- document.run_callbacks :after_create
122
+ document.run_callbacks :save do
123
+ document.run_callbacks :create do
124
+ res = database.save_doc document.to_hash
125
+ document._rev = res['rev']
126
+ document._id = res['id']
127
+ end
128
+ end
127
129
  true
128
130
  end
129
131
 
130
132
  def update_document(document, validate)
131
133
  if validate
132
134
  document.errors.clear
133
- document.run_callbacks :before_validation_on_save
134
- document.run_callbacks :before_validation_on_update
135
- return false unless valid_document?(document)
135
+ document.run_callbacks :validation_on_save do
136
+ document.run_callbacks :validation_on_update do
137
+ return false unless valid_document?(document)
138
+ end
139
+ end
140
+ end
141
+
142
+ document.run_callbacks :save do
143
+ document.run_callbacks :update do
144
+ res = database.save_doc document.to_hash
145
+ document._rev = res['rev']
146
+ end
136
147
  end
137
-
138
- document.run_callbacks :before_save
139
- document.run_callbacks :before_update
140
- res = database.save_doc document.to_hash
141
- document._rev = res['rev']
142
- document.run_callbacks :after_save
143
- document.run_callbacks :after_update
144
148
  true
145
149
  end
146
150
 
@@ -158,4 +162,4 @@ module CouchPotato
158
162
  end
159
163
 
160
164
  end
161
- end
165
+ end
@@ -18,9 +18,10 @@ module CouchPotato
18
18
  module Persistence
19
19
 
20
20
  def self.included(base) #:nodoc:
21
- base.send :include, Properties, Callbacks, Validation, Json, CouchPotato::View::CustomViews, CouchPotato::View::Lists
21
+ base.send :include, Properties, Callbacks, Json, CouchPotato::View::CustomViews, CouchPotato::View::Lists
22
22
  base.send :include, DirtyAttributes, GhostAttributes, Attachments
23
23
  base.send :include, MagicTimestamps, ActiveModelCompliance
24
+ base.send :include, Validation
24
25
  base.class_eval do
25
26
  attr_accessor :_id, :_rev, :_deleted, :database
26
27
  alias_method :id, :_id
@@ -29,7 +30,8 @@ module CouchPotato
29
30
  end
30
31
 
31
32
  # initialize a new instance of the model optionally passing it a hash of attributes.
32
- # the attributes have to be declared using the #property method
33
+ # the attributes have to be declared using the #property method.
34
+ # the new model will be yielded to an optionally given block.
33
35
  #
34
36
  # example:
35
37
  # class Book
@@ -37,11 +39,20 @@ module CouchPotato
37
39
  # property :title
38
40
  # end
39
41
  # book = Book.new :title => 'Time to Relax'
42
+ #
43
+ # OR
44
+ #
45
+ # book = Book.new do |b|
46
+ # b.title = 'Time to Relax'
47
+ # end
40
48
  # book.title # => 'Time to Relax'
41
49
  def initialize(attributes = {})
42
- attributes.each do |name, value|
43
- self.send("#{name}=", value)
44
- end if attributes
50
+ if attributes
51
+ attributes.each do |name, value|
52
+ self.send("#{name}=", value)
53
+ end
54
+ end
55
+ yield self if block_given?
45
56
  end
46
57
 
47
58
  # assign multiple attributes at once.
@@ -96,6 +107,14 @@ module CouchPotato
96
107
  def ==(other) #:nodoc:
97
108
  other.class == self.class && self.to_json == other.to_json
98
109
  end
110
+
111
+ def eql?(other)
112
+ self == other
113
+ end
114
+
115
+ def hash
116
+ _id.hash * (_id.hash.to_s.size ** 10) + _rev.hash
117
+ end
99
118
 
100
119
  def inspect
101
120
  attributes_as_string = attributes.map {|attribute, value| "#{attribute}: #{value.inspect}"}.join(", ")
@@ -1,62 +1,29 @@
1
+ require 'active_support/concern'
2
+ require 'active_model/callbacks'
3
+
1
4
  module CouchPotato
2
5
  module Persistence
3
6
  module Callbacks
4
7
  def self.included(base) #:nodoc:
5
- base.extend ClassMethods
8
+ base.extend ActiveModel::Callbacks
6
9
 
7
10
  base.class_eval do
8
11
  attr_accessor :skip_callbacks
9
- def self.callbacks #:nodoc:
10
- @callbacks ||= {:before_validation => [], :before_validation_on_create => [],
11
- :before_validation_on_update => [], :before_validation_on_save => [], :before_create => [],
12
- :after_create => [], :before_update => [], :after_update => [],
13
- :before_save => [], :after_save => [],
14
- :before_destroy => [], :after_destroy => []}
15
- end
12
+
13
+ define_model_callbacks :create, :save, :update, :destroy
14
+ define_model_callbacks *[:save, :create, :update].map {|c| :"validation_on_#{c}"}
15
+ define_model_callbacks :validation unless Config.validation_framework == :active_model
16
16
  end
17
17
  end
18
18
 
19
19
  # Runs all callbacks on a model with the given name, e.g. :after_create.
20
20
  #
21
21
  # This method is called by the CouchPotato::Database object when saving/destroying an object
22
- def run_callbacks(name)
22
+ def run_callbacks(name, &block)
23
23
  return if skip_callbacks
24
-
25
- callbacks = self.class.ancestors.map do |clazz|
26
- clazz.callbacks[name] if clazz.respond_to?(:callbacks)
27
- end.flatten.compact.uniq
28
-
29
- callbacks.each do |callback|
30
- if [Symbol, String].include?(callback.class)
31
- send callback
32
- elsif callback.is_a?(Proc)
33
- callback.call self
34
- else
35
- raise "Don't know how to handle callback of type #{callback.class.name}"
36
- end
37
- end
38
- end
39
24
 
40
- module ClassMethods
41
- [
42
- :before_validation,
43
- :before_validation_on_create,
44
- :before_validation_on_update,
45
- :before_validation_on_save,
46
- :before_create,
47
- :before_save,
48
- :before_update,
49
- :before_destroy,
50
- :after_update,
51
- :after_save,
52
- :after_create,
53
- :after_destroy
54
- ].each do |callback|
55
- define_method callback do |*names|
56
- callbacks[callback].push *names
57
- end
58
- end
25
+ send(:"_run_#{name}_callbacks", &block)
59
26
  end
60
27
  end
61
28
  end
62
- end
29
+ end
@@ -4,21 +4,20 @@ module CouchPotato
4
4
  module DirtyAttributes
5
5
 
6
6
  def self.included(base) #:nodoc:
7
+ base.send :include, ActiveModel::Dirty
7
8
  base.class_eval do
8
9
  after_save :reset_dirty_attributes
9
-
10
- def initialize(attributes = {})
11
- super
12
- assign_attribute_copies_for_dirty_tracking
13
- end
14
10
  end
15
11
  end
16
12
 
13
+ def initialize(attributes = {})
14
+ super
15
+ # assign_attribute_copies_for_dirty_tracking
16
+ end
17
+
17
18
  # returns true if a model has dirty attributes, i.e. their value has changed since the last save
18
19
  def dirty?
19
- new? || @forced_dirty || self.class.properties.inject(false) do |res, property|
20
- res || property.dirty?(self)
21
- end
20
+ changed? || @forced_dirty
22
21
  end
23
22
 
24
23
  # marks a model as dirty
@@ -26,19 +25,21 @@ module CouchPotato
26
25
  @forced_dirty = true
27
26
  end
28
27
 
29
- private
30
-
31
- def assign_attribute_copies_for_dirty_tracking
32
- attributes.each do |name, value|
33
- self.instance_variable_set("@#{name}_was", clone_attribute(value))
34
- end if attributes
28
+ def method_missing(name, *args)
29
+ if(name.to_s.include?('_will_change!'))
30
+ self.class.define_attribute_methods self.class.property_names
31
+ send(name, *args)
32
+ else
33
+ super
34
+ end
35
35
  end
36
36
 
37
+ private
38
+
37
39
  def reset_dirty_attributes
40
+ @previously_changed = changes
41
+ @changed_attributes.clear
38
42
  @forced_dirty = nil
39
- self.class.properties.each do |property|
40
- instance_variable_set("@#{property.name}_was", clone_attribute(send(property.name)))
41
- end
42
43
  end
43
44
 
44
45
  def clone_attribute(value)
@@ -3,20 +3,25 @@ module CouchPotato
3
3
  def self.included(base)
4
4
  base.class_eval do
5
5
  attr_accessor :_document
6
- def self.json_create(json)
7
- instance = super
6
+ def self.json_create_with_ghost(json)
7
+ instance = json_create_without_ghost(json)
8
8
  instance._document = json if json
9
9
  instance
10
10
  end
11
11
 
12
- def method_missing(name, *args)
13
- if(value = _document && _document[name.to_s])
14
- value
15
- else
16
- super
17
- end
12
+ class << self
13
+ alias_method_chain :json_create, :ghost
18
14
  end
19
15
  end
20
16
  end
17
+
18
+ def method_missing(name, *args)
19
+ if(value = _document && _document[name.to_s])
20
+ value
21
+ else
22
+ super
23
+ end
24
+ end
21
25
  end
22
- end
26
+ end
27
+
@@ -35,9 +35,12 @@ module CouchPotato
35
35
  instance = self.new
36
36
  instance._id = json[:_id] || json['_id']
37
37
  instance._rev = json[:_rev] || json['_rev']
38
+ instance.instance_variable_set('@skip_dirty_tracking', true)
38
39
  properties.each do |property|
39
40
  property.build(instance, json)
40
41
  end
42
+ instance.instance_variable_set('@skip_dirty_tracking', false)
43
+ # instance.instance_variable_get("@changed_attributes").clear if instance.instance_variable_get("@changed_attributes")
41
44
  instance
42
45
  end
43
46
  end
@@ -7,13 +7,14 @@ module CouchPotato
7
7
 
8
8
  before_create lambda {|model|
9
9
  model.created_at ||= Time.now
10
- model.created_at_not_changed
10
+ @changed_attributes.delete 'created_at'
11
11
  model.updated_at ||= Time.now
12
- model.updated_at_not_changed
12
+ @changed_attributes.delete 'updated_at'
13
13
  }
14
14
  before_update lambda {|model|
15
15
  model.updated_at = Time.now
16
- model.updated_at_not_changed}
16
+ @changed_attributes.delete 'updated_at'
17
+ }
17
18
  end
18
19
  end
19
20
  end
@@ -59,13 +59,6 @@ module CouchPotato
59
59
  properties.map(&:name)
60
60
  end
61
61
 
62
- def json_create(json) #:nodoc:
63
- return if json.nil?
64
- instance = super
65
- instance.send(:assign_attribute_copies_for_dirty_tracking)
66
- instance
67
- end
68
-
69
62
  # Declare a property on a model class. Properties are not typed by default.
70
63
  # You can store anything in a property that can be serialized into JSON.
71
64
  # If you want a property to be of a custom class you have to define it using the :type option.
@@ -37,8 +37,6 @@ module CouchPotato
37
37
 
38
38
  def define_accessors(base, name, options)
39
39
  base.class_eval do
40
- attr_reader "#{name}_was"
41
-
42
40
  define_method "#{name}" do
43
41
  value = self.instance_variable_get("@#{name}")
44
42
  if value.nil? && options[:default]
@@ -51,20 +49,14 @@ module CouchPotato
51
49
  end
52
50
 
53
51
  define_method "#{name}=" do |value|
54
- self.instance_variable_set("@#{name}", type_caster.cast(value, options[:type]))
52
+ typecasted_value = type_caster.cast(value, options[:type])
53
+ send("#{name}_will_change!") unless @skip_dirty_tracking || typecasted_value == send(name)
54
+ self.instance_variable_set("@#{name}", typecasted_value)
55
55
  end
56
56
 
57
57
  define_method "#{name}?" do
58
58
  !self.send(name).nil? && !self.send(name).try(:blank?)
59
59
  end
60
-
61
- define_method "#{name}_changed?" do
62
- !self.instance_variable_get("@#{name}_not_changed") && self.send(name) != self.send("#{name}_was")
63
- end
64
-
65
- define_method "#{name}_not_changed" do
66
- self.instance_variable_set("@#{name}_not_changed", true)
67
- end
68
60
  end
69
61
  end
70
62
  end
@@ -3,15 +3,15 @@ module CouchPotato
3
3
  class TypeCaster #:nodoc:
4
4
  def cast(value, type)
5
5
  if type == :boolean
6
- cast_boolen(value)
6
+ cast_boolean(value)
7
7
  else
8
8
  cast_native(value, type)
9
9
  end
10
10
  end
11
-
11
+
12
12
  private
13
-
14
- def cast_boolen(value)
13
+
14
+ def cast_boolean(value)
15
15
  if [FalseClass, TrueClass].include?(value.class) || value.nil?
16
16
  value
17
17
  elsif [0, '0'].include?(value)
@@ -20,13 +20,13 @@ module CouchPotato
20
20
  true
21
21
  end
22
22
  end
23
-
23
+
24
24
  def cast_native(value, type)
25
25
  if type && !value.instance_of?(type)
26
26
  if type == Fixnum
27
- value.to_s.scan(/\d/).join.to_i unless value.blank?
27
+ value.to_s.scan(/-?\d+/).join.to_i unless value.blank?
28
28
  elsif type == Float
29
- value.to_s.scan(/\d+\.?\d*/).join.to_f unless value.blank?
29
+ value.to_s.scan(/-?\d+\.?\d*/).join.to_f unless value.blank?
30
30
  else
31
31
  type.json_create value unless value.blank?
32
32
  end
@@ -34,7 +34,7 @@ module CouchPotato
34
34
  value
35
35
  end
36
36
  end
37
-
37
+
38
38
  end
39
39
  end
40
- end
40
+ end
@@ -1,18 +1,24 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../../rails/reload_classes')
2
+ require 'erb'
2
3
 
3
4
  module CouchPotato
4
5
  def self.rails_init
5
- CouchPotato::Config.database_name = YAML::load(File.read(Rails.root.join('config/couchdb.yml')))[Rails.env]
6
+ config = YAML::load(ERB.new(File.read(Rails.root.join('config/couchdb.yml'))).result)[RAILS_ENV]
7
+ if config.is_a?(String)
8
+ CouchPotato::Config.database_name = config
9
+ else
10
+ CouchPotato::Config.database_name = config['database']
11
+ CouchPotato::Config.validation_framework = config['validation_framework']
12
+ end
6
13
  end
7
14
 
8
15
  if defined?(::Rails::Railtie)
9
16
  class Railtie < ::Rails::Railtie
10
- config.after_initialize do |app|
17
+ initializer 'couch_potato.load_config' do |app|
11
18
  CouchPotato.rails_init
12
19
  end
13
20
  end
14
21
  else
15
22
  rails_init
16
23
  end
17
-
18
24
  end
@@ -11,16 +11,20 @@ module CouchPotato
11
11
  base.send :include, ::Validatable
12
12
  base.class_eval do
13
13
  # Override the validate method to first run before_validation callback
14
- def valid?
14
+ def valid_with_before_validation_callback?
15
15
  errors.clear
16
- run_callbacks :before_validation
17
- before_validation_errors = errors.errors.dup
18
- super
19
- before_validation_errors.each do |k, v|
20
- v.each {|message| errors.add(k, message)}
16
+ run_callbacks :validation do
17
+ before_validation_errors = errors.errors.dup
18
+ valid_without_before_validation_callback?
19
+ before_validation_errors.each do |k, v|
20
+ v.each {|message| errors.add(k, message)}
21
+ end
21
22
  end
22
23
  errors.empty?
23
24
  end
25
+
26
+ alias_method :valid_without_before_validation_callback?, :valid?
27
+ alias_method :valid?, :valid_with_before_validation_callback?
24
28
  end
25
29
  end
26
30
  end
@@ -1,4 +1,16 @@
1
1
  module CouchPotato
2
+ # We can bypass creation of views completely, so we can use views
3
+ # that are created via other means, such as CouchApp.
4
+ # The default is not to bypass creation.
5
+ # TODO: enable this bypass for specific view in the view specification, instead of globally
6
+ def self.bypass_view_creation
7
+ @bypass_view_creation
8
+ end
9
+
10
+ def self.bypass_view_creation= flag
11
+ @bypass_view_creation = flag
12
+ end
13
+
2
14
  module View
3
15
  # Used to query views (and create them if they don't exist). Usually you won't have to use this class directly. Instead it is used internally by the CouchPotato::Database.view method.
4
16
  class ViewQuery
@@ -15,7 +27,7 @@ module CouchPotato
15
27
  end
16
28
 
17
29
  def query_view!(parameters = {})
18
- update_view unless view_has_been_updated?
30
+ update_view unless CouchPotato::bypass_view_creation || view_has_been_updated?
19
31
  begin
20
32
  query_view parameters
21
33
  rescue RestClient::ResourceNotFound
@@ -37,7 +49,9 @@ module CouchPotato
37
49
  design_doc['lists'] ||= {}
38
50
  design_doc['lists'][@list_name.to_s] = @list_function
39
51
  end
40
- @database.save_doc(design_doc) if original_views != design_doc['views'] || original_lists != design_doc['lists']
52
+ # We do not save the design document if view creation is bypassed
53
+ @database.save_doc(design_doc) unless CouchPotato::bypass_view_creation ||
54
+ original_views == design_doc['views'] && original_lists == design_doc['lists']
41
55
  end
42
56
 
43
57
  def view_functions
@@ -49,14 +63,17 @@ module CouchPotato
49
63
  end
50
64
 
51
65
  def view_has_been_updated?
66
+ # TODO: decide what to do here in case CouchPotato::bypass_view_creation is true
52
67
  updated_views[[@design_document_name, @view_name]]
53
68
  end
54
69
 
55
70
  def view_updated
71
+ # TODO: decide what to do here in case CouchPotato::bypass_view_creation is true
56
72
  updated_views[[@design_document_name, @view_name]] = true
57
73
  end
58
74
 
59
75
  def updated_views
76
+ # TODO: decide what to do here in case CouchPotato::bypass_view_creation is true
60
77
  @@updated_views ||= {}
61
78
  @@updated_views
62
79
  end
@@ -7,7 +7,7 @@ module CouchPotato
7
7
  begin
8
8
  yield
9
9
  rescue ArgumentError => e
10
- if(name = e.message.scan(/(can't find const|undefined class\/module) ([\w\:]+)/).first[1])
10
+ if(name = e.message.scan(/(can't find const|undefined class\/module) ([\w\:]+)/).try(:first).try(:[], 1))
11
11
  eval name.gsub(/\:+$/, '')
12
12
  retry
13
13
  else
@@ -22,9 +22,6 @@ class CallbackRecorder
22
22
 
23
23
  view :all, :key => :required_property
24
24
 
25
- attr_accessor :lambda_works
26
- before_create lambda {|model| model.lambda_works = true }
27
-
28
25
  def callbacks
29
26
  @callbacks ||= []
30
27
  end
@@ -58,7 +55,7 @@ describe "multiple callbacks at once" do
58
55
 
59
56
  it "should run all callback methods given to the callback method call" do
60
57
  monkey = Monkey.new
61
- monkey.run_callbacks :before_create
58
+ monkey.run_callbacks :create
62
59
  monkey.eaten_banana.should be_true
63
60
  monkey.eaten_apple.should be_true
64
61
  end
@@ -263,14 +260,6 @@ describe "destroy callbacks" do
263
260
  end
264
261
  end
265
262
 
266
- describe "lambda callbacks" do
267
- it "should run the lambda" do
268
- recorder = CallbackRecorder.new
269
- recorder.run_callbacks :before_create
270
- recorder.lambda_works.should be_true
271
- end
272
- end
273
-
274
263
  describe "validation callbacks" do
275
264
  class ValidatedUser
276
265
  include CouchPotato::Persistence
@@ -305,4 +294,4 @@ describe "validation callbacks" do
305
294
  user.valid?.should == true
306
295
  user.errors.on(:name).should == nil
307
296
  end
308
- end
297
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
- require File.join(File.dirname(__FILE__), 'fixtures', 'address')
3
- require File.join(File.dirname(__FILE__), 'fixtures', 'person')
2
+ require 'fixtures/address'
3
+ require 'fixtures/person'
4
4
 
5
5
  class Watch
6
6
  include CouchPotato::Persistence
data/spec/rails_spec.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require File.dirname(__FILE__) + '/../rails/reload_classes'
3
2
 
4
3
  module LoadConst
5
4
  def const_missing(name)
@@ -33,7 +32,7 @@ describe CouchPotato::Database, 'rails specific behavior' do
33
32
  CouchPotato.couchrest_database.save_doc(JSON.create_id => 'Autoloader::Uninitialized', '_id' => '1')
34
33
  CouchPotato.database.load('1').class.name.should == 'Autoloader::Uninitialized'
35
34
  end
36
-
35
+
37
36
  it "should load nested models" do
38
37
  CouchPotato.couchrest_database.save_doc(JSON.create_id => 'Autoloader::Nested::Nested2', '_id' => '1')
39
38
  CouchPotato.database.load('1').class.name.should == 'Autoloader::Nested::Nested2'
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+ require 'spec/mocks'
4
+
5
+ RAILS_ENV = 'test'
6
+ module Rails
7
+ class Railtie
8
+ def self.initializer(*args)
9
+ end
10
+ end
11
+
12
+ def self.root
13
+ Spec::Mocks::Mock.new :join => ''
14
+ end
15
+ end
16
+
17
+ require 'couch_potato/railtie'
18
+
19
+ describe "railtie" do
20
+ before(:all) do
21
+ @validation_framework = CouchPotato::Config.validation_framework
22
+ end
23
+
24
+ after(:all) do
25
+ CouchPotato::Config.validation_framework = @validation_framework
26
+ end
27
+
28
+ context 'yaml file contains only database names' do
29
+ it "should set the database name from the yaml file" do
30
+ File.stub(:read => "test: test_db")
31
+
32
+ CouchPotato::Config.should_receive(:database_name=).with('test_db')
33
+
34
+ CouchPotato.rails_init
35
+ end
36
+ end
37
+
38
+ context 'yaml file contains more configuration' do
39
+ before(:each) do
40
+ File.stub(:read => "test: \n database: test_db\n validation_framework: :active_model")
41
+ end
42
+
43
+ it "should set the database name from the yaml file" do
44
+ CouchPotato::Config.should_receive(:database_name=).with('test_db')
45
+
46
+ CouchPotato.rails_init
47
+ end
48
+
49
+ it "should set the validation framework from the yaml file" do
50
+ CouchPotato::Config.should_receive(:validation_framework=).with(:active_model)
51
+
52
+ CouchPotato.rails_init
53
+ end
54
+ end
55
+
56
+ it "should process the yml file with erb" do
57
+ File.stub(:read => "test: \n database: <%= 'db' %>")
58
+
59
+ CouchPotato::Config.should_receive(:database_name=).with('db')
60
+
61
+ CouchPotato.rails_init
62
+ end
63
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'spec'
3
+ require 'time'
3
4
 
4
5
  $:.unshift(File.dirname(__FILE__) + '/../lib')
5
6
 
@@ -58,6 +58,11 @@ describe "attributes" do
58
58
  @plant.typed_leaf_count.should == 4
59
59
  end
60
60
 
61
+ it "should convert a string into a negative fixnum" do
62
+ @plant.typed_leaf_count = '-4'
63
+ @plant.typed_leaf_count.should == -4
64
+ end
65
+
61
66
  it "should leave a fixnum as is" do
62
67
  @plant.typed_leaf_count = 4
63
68
  @plant.typed_leaf_count.should == 4
@@ -94,6 +99,11 @@ describe "attributes" do
94
99
  @plant.typed_leaf_size = '5'
95
100
  @plant.typed_leaf_size.should == 5.0
96
101
  end
102
+
103
+ it "should convert a negative number in a string" do
104
+ @plant.typed_leaf_size = '-5.0'
105
+ @plant.typed_leaf_size.should == -5.0
106
+ end
97
107
 
98
108
  it "should leave a float as it is" do
99
109
  @plant.typed_leaf_size = 0.5
@@ -4,23 +4,15 @@ describe 'callbacks' do
4
4
  class Tree
5
5
  include CouchPotato::Persistence
6
6
 
7
- before_validation :grow_leaf, 'grow_branch', lambda {|tree| tree.root_count ||= 0; tree.root_count += 1 }
7
+ before_validation :grow_leaf
8
8
 
9
9
  property :leaf_count
10
- property :root_count
11
- property :branch_count
12
10
  property :watered
13
-
14
11
 
15
12
  def grow_leaf
16
13
  self.leaf_count ||= 0
17
14
  self.leaf_count += 1
18
15
  end
19
-
20
- def grow_branch
21
- self.branch_count ||= 0
22
- self.branch_count += 1
23
- end
24
16
  end
25
17
 
26
18
  class AppleTree < Tree
@@ -37,31 +29,13 @@ describe 'callbacks' do
37
29
  end
38
30
  end
39
31
 
40
- it "should call a method from a symbol when validated" do
41
- tree = Tree.new(:leaf_count => 1, :root_count => 1)
42
- tree.valid?
43
- tree.leaf_count.should == 2
44
- end
45
-
46
- it "should call a method from a string when validated" do
47
- tree = Tree.new(:branch_count => 0)
48
- tree.valid?
49
- tree.branch_count.should == 1
50
- end
51
-
52
- it "should call a lambda when validated" do
53
- tree = Tree.new(:leaf_count => 1, :root_count => 1)
54
- tree.valid?
55
- tree.root_count.should == 2
56
- end
57
-
58
32
  context 'inheritance' do
59
33
  it "should call the callbacks of the super class" do
60
34
  tree = AppleTree.new :leaf_count => 1
61
35
  tree.valid?
62
36
  tree.leaf_count.should == 2
63
37
  end
64
-
38
+
65
39
  it "should call the callbacks of the child class" do
66
40
  tree = AppleTree.new :leaf_count => 1
67
41
  tree.valid?
@@ -69,4 +43,4 @@ describe 'callbacks' do
69
43
  end
70
44
  end
71
45
 
72
- end
46
+ end
@@ -119,10 +119,10 @@ describe CouchPotato::Database, 'save_document' do
119
119
  it "should not run the validations when saved with false" do
120
120
  category = Category.new(:name => 'food')
121
121
  @db.save_document(category)
122
- category.new?.should == false
122
+ category.new?.should be_false
123
123
  category.name = nil
124
124
  @db.save_document(category, false)
125
- category.dirty?.should == false
125
+ category.dirty?.should be_false
126
126
  end
127
127
 
128
128
  it "should run the validations when saved with true" do
@@ -7,10 +7,10 @@ describe Date, 'to_json' do
7
7
  end
8
8
  end
9
9
 
10
- describe Date, 'to_s(:json)' do
10
+ describe Date, 'as_json' do
11
11
  it "should format it in the same way as to_json does so i can use this to do queries over date attributes" do
12
12
  date = Date.parse('2009-01-01')
13
- date.to_s(:json).should == "2009/01/01"
13
+ date.as_json.should == "2009/01/01"
14
14
  end
15
15
  end
16
16
 
@@ -33,24 +33,6 @@ describe 'dirty attribute tracking' do
33
33
  @couchrest_db.should_receive(:save_doc)
34
34
  @db.save_document(plate)
35
35
  end
36
-
37
- it "should correctly track dirty hashes (deep clone)" do
38
- plate = Plate.new :food => {:veggies => ['carrots', 'peas']}
39
- @db.save_document(plate)
40
- plate.food[:veggies] << 'beans'
41
- @couchrest_db.should_receive(:save_doc)
42
- @db.save_document(plate)
43
- end
44
-
45
- it "should correctly track dirty hashes (deep clone) after a save" do
46
- plate = Plate.new :food => {:veggies => ['carrots', 'peas']}
47
- @db.save_document(plate)
48
- plate.food[:veggies] << 'beans'
49
- @db.save_document(plate)
50
- plate.food[:veggies] << 'cauliflower'
51
- @couchrest_db.should_receive(:save_doc)
52
- @db.save_document(plate)
53
- end
54
36
  end
55
37
 
56
38
  describe "newly created object" do
@@ -67,21 +49,22 @@ describe 'dirty attribute tracking' do
67
49
 
68
50
  describe "with type BigDecimal" do
69
51
  before(:each) do
70
- class ::Plate
52
+ class Bowl
53
+ include CouchPotato::Persistence
71
54
  property :price
72
55
  end
73
56
  end
74
57
  it "should not dup BigDecimal" do
75
58
 
76
59
  lambda {
77
- Plate.new :price => BigDecimal.new("5.23")
60
+ Bowl.new :price => BigDecimal.new("5.23")
78
61
  }.should_not raise_error(TypeError)
79
62
  end
80
63
 
81
64
  it "should return the old value" do
82
- plate = Plate.new :price => BigDecimal.new("5.23")
83
- plate.price = BigDecimal.new("2.23")
84
- plate.price_was.should == 5.23
65
+ bowl = Bowl.new :price => BigDecimal.new("5.23")
66
+ bowl.price = BigDecimal.new("2.23")
67
+ bowl.price_was.should == 5.23
85
68
  end
86
69
 
87
70
  end
@@ -94,13 +77,7 @@ describe 'dirty attribute tracking' do
94
77
  end
95
78
 
96
79
  it "should return false if attribute not changed" do
97
- @plate.should_not be_food_changed
98
- end
99
-
100
- it "should return false if attribute forced not changed" do
101
- @plate.food = 'burger'
102
- @plate.food_not_changed
103
- @plate.should_not be_food_changed
80
+ Plate.new.should_not be_food_changed
104
81
  end
105
82
 
106
83
  it "should return true if forced dirty" do
@@ -128,13 +105,6 @@ describe 'dirty attribute tracking' do
128
105
  @plate.food = 'burger'
129
106
  @plate.should be_food_changed
130
107
  end
131
-
132
- it "should return true if array attribute changed" do
133
- couchrest_db = stub('database', :get => Plate.json_create({'_id' => '1', '_rev' => '2', 'food' => ['sushi'], JSON.create_id => 'Plate'}), :info => nil)
134
- plate = CouchPotato::Database.new(couchrest_db).load_document '1'
135
- plate.food << 'burger'
136
- plate.should be_food_changed
137
- end
138
108
 
139
109
  it "should return false if attribute not changed" do
140
110
  @plate.should_not be_food_changed
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ class Document
4
+ include CouchPotato::Persistence
5
+
6
+ property :title
7
+ property :content
8
+ end
9
+
10
+ describe "new" do
11
+ context "without arguments" do
12
+ subject { Document.new }
13
+
14
+ it { should be_a(Document) }
15
+ its(:title) { should be_nil }
16
+ its(:content) { should be_nil }
17
+ end
18
+
19
+ context "with an argument hash" do
20
+ subject { Document.new(:title => 'My Title') }
21
+
22
+ it { should be_a(Document) }
23
+ its(:title) { should == 'My Title'}
24
+ its(:content) { should be_nil }
25
+ end
26
+
27
+ context "yielding to a block" do
28
+ subject {
29
+ Document.new(:title => 'My Title') do |doc|
30
+ doc.content = 'My Content'
31
+ end
32
+ }
33
+
34
+ it { should be_a(Document) }
35
+ its(:title) { should == 'My Title'}
36
+ its(:content) { should == 'My Content'}
37
+ end
38
+ end
@@ -7,10 +7,10 @@ describe Time, 'to_json' do
7
7
  end
8
8
  end
9
9
 
10
- describe Time, 'to_s(:json)' do
10
+ describe Time, 'as_json' do
11
11
  it "should format it in the same way as to_json does so i can use this to do queries over time attributes" do
12
12
  time = Time.parse('2009-01-01 11:12:23 +0200')
13
- time.to_s(:json).should == "2009/01/01 09:12:23 +0000"
13
+ time.as_json.should == "2009/01/01 09:12:23 +0000"
14
14
  end
15
15
  end
16
16
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: davber_couch_potato
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - David Bergman
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-14 00:00:00 -04:00
18
+ date: 2011-02-04 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -40,13 +40,28 @@ dependencies:
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- hash: 59
43
+ hash: 21
44
44
  segments:
45
+ - 1
45
46
  - 0
46
- - 24
47
- version: "0.24"
47
+ - 1
48
+ version: 1.0.1
48
49
  type: :runtime
49
50
  version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: activemodel
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :runtime
64
+ version_requirements: *id003
50
65
  description: Ruby persistence layer for CouchDB
51
66
  email: davber@gmail.com
52
67
  executables: []
@@ -111,6 +126,7 @@ files:
111
126
  - spec/fixtures/person.rb
112
127
  - spec/property_spec.rb
113
128
  - spec/rails_spec.rb
129
+ - spec/railtie_spec.rb
114
130
  - spec/spec.opts
115
131
  - spec/spec_helper.rb
116
132
  - spec/unit/active_model_compliance_spec.rb
@@ -123,6 +139,7 @@ files:
123
139
  - spec/unit/database_spec.rb
124
140
  - spec/unit/date_spec.rb
125
141
  - spec/unit/dirty_attributes_spec.rb
142
+ - spec/unit/initialize_spec.rb
126
143
  - spec/unit/json_create_id_spec.rb
127
144
  - spec/unit/lists_spec.rb
128
145
  - spec/unit/model_view_spec_spec.rb
@@ -180,6 +197,7 @@ test_files:
180
197
  - spec/fixtures/person.rb
181
198
  - spec/property_spec.rb
182
199
  - spec/rails_spec.rb
200
+ - spec/railtie_spec.rb
183
201
  - spec/spec_helper.rb
184
202
  - spec/unit/active_model_compliance_spec.rb
185
203
  - spec/unit/attributes_spec.rb
@@ -191,6 +209,7 @@ test_files:
191
209
  - spec/unit/database_spec.rb
192
210
  - spec/unit/date_spec.rb
193
211
  - spec/unit/dirty_attributes_spec.rb
212
+ - spec/unit/initialize_spec.rb
194
213
  - spec/unit/json_create_id_spec.rb
195
214
  - spec/unit/lists_spec.rb
196
215
  - spec/unit/model_view_spec_spec.rb