mattetti-couchrest 0.13.1 → 0.13.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -65,4 +65,35 @@ with CouchDB in your Rails or Merb app is no harder than working with the
65
65
  standard SQL alternatives. See the CouchRest::Model documentation for an
66
66
  example article class that illustrates usage.
67
67
 
68
- CouchRest::Model will be removed from this package.
68
+ CouchRest::Model will be removed from this package.
69
+
70
+
71
+ ## CouchRest::ExtendedDocument
72
+
73
+ ### Callbacks
74
+
75
+ `CouchRest::ExtendedDocuments` instances have 2 callbacks already defined for you:
76
+ `create_callback`, `save_callback`, `update_callback` and `destroy_callback`
77
+
78
+ In your document inherits from `CouchRest::ExtendedDocument`, define your callback as follows:
79
+
80
+ save_callback :before, :generate_slug_from_name
81
+
82
+ CouchRest uses a mixin you can find in lib/mixins/callbacks which is extracted from Rails 3, here are some simple usage examples:
83
+
84
+ save_callback :before, :before_method
85
+ save_callback :after, :after_method, :if => :condition
86
+ save_callback :around {|r| stuff; yield; stuff }
87
+
88
+ Check the mixin or the ExtendedDocument class to see how to implement your own callbacks.
89
+
90
+ ### Casting
91
+
92
+ Often, you will want to store multiple objects within a document, to be able to retrieve your objects when you load the document,
93
+ you can define some casting rules.
94
+
95
+ property :casted_attribute, :cast_as => 'WithCastedModelMixin'
96
+ property :keywords, :cast_as => ["String"]
97
+
98
+ If you want to cast an array of instances from a specific Class, use the trick shown above ["ClassName"]
99
+
data/Rakefile CHANGED
@@ -23,7 +23,7 @@ spec = Gem::Specification.new do |s|
23
23
  s.homepage = "http://github.com/jchris/couchrest"
24
24
  s.description = "CouchRest provides a simple interface on top of CouchDB's RESTful HTTP API, as well as including some utility scripts for managing views and attachments."
25
25
  s.has_rdoc = true
26
- s.authors = ["J. Chris Anderson"]
26
+ s.authors = ["J. Chris Anderson", "Matt Aimonetti"]
27
27
  s.files = %w( LICENSE README.md Rakefile THANKS.md ) +
28
28
  Dir["{examples,lib,spec,utils}/**/*"] -
29
29
  Dir["spec/tmp"]
@@ -40,8 +40,8 @@ module CouchRest
40
40
  key = self.has_key?(property.name) ? property.name : property.name.to_sym
41
41
  target = property.type
42
42
  if target.is_a?(Array)
43
+ next unless self[key]
43
44
  klass = ::CouchRest.constantize(target[0])
44
-
45
45
  self[property.name] = self[key].collect do |value|
46
46
  # Auto parse Time objects
47
47
  obj = ( (property.init_method == 'new') && klass == Time) ? Time.parse(value) : klass.send(property.init_method, value)
@@ -56,47 +56,58 @@ module RestClient
56
56
  :url => url,
57
57
  :headers => headers)
58
58
  end
59
-
60
- class Request
61
- def transmit(uri, req, payload)
62
- setup_credentials(req)
63
-
64
- Thread.current[:host] ||= uri.host
65
- Thread.current[:port] ||= uri.port
66
-
67
- net = net_http_class.new(uri.host, uri.port)
68
-
69
- if Thread.current[:connection].nil? || Thread.current[:host] != uri.host
70
- Thread.current[:connection].finish if (Thread.current[:connection] && Thread.current[:connection].started?)
71
- net.use_ssl = uri.is_a?(URI::HTTPS)
72
- net.verify_mode = OpenSSL::SSL::VERIFY_NONE
73
- Thread.current[:connection] = net
74
- Thread.current[:connection].start
75
- end
76
-
77
- display_log request_log
78
- http = Thread.current[:connection]
79
59
 
80
- http.read_timeout = @timeout if @timeout
81
- begin
82
- res = http.request(req, payload)
83
- rescue
84
- # p "Net::HTTP connection failed, reconnecting"
85
- Thread.current[:connection].finish
86
- http = Thread.current[:connection] = net
87
- Thread.current[:connection].start
88
- res = http.request(req, payload)
89
- display_log response_log(res)
90
- process_result res
91
- else
92
- display_log response_log(res)
93
- process_result res
94
- end
95
-
96
- rescue EOFError
97
- raise RestClient::ServerBrokeConnection
98
- rescue Timeout::Error
99
- raise RestClient::RequestTimeout
100
- end
101
- end
60
+ # class Request
61
+ #
62
+ # def establish_connection(uri)
63
+ # Thread.current[:connection].finish if (Thread.current[:connection] && Thread.current[:connection].started?)
64
+ # p net_http_class
65
+ # net = net_http_class.new(uri.host, uri.port)
66
+ # net.use_ssl = uri.is_a?(URI::HTTPS)
67
+ # net.verify_mode = OpenSSL::SSL::VERIFY_NONE
68
+ # Thread.current[:connection] = net
69
+ # Thread.current[:connection].start
70
+ # Thread.current[:connection]
71
+ # end
72
+ #
73
+ # def transmit(uri, req, payload)
74
+ # setup_credentials(req)
75
+ #
76
+ # Thread.current[:host] ||= uri.host
77
+ # Thread.current[:port] ||= uri.port
78
+ #
79
+ # if (Thread.current[:connection].nil? || (Thread.current[:host] != uri.host))
80
+ # p "establishing a connection"
81
+ # establish_connection(uri)
82
+ # end
83
+ #
84
+ # display_log request_log
85
+ # http = Thread.current[:connection]
86
+ # http.read_timeout = @timeout if @timeout
87
+ #
88
+ # begin
89
+ # res = http.request(req, payload)
90
+ # rescue
91
+ # p "Net::HTTP connection failed, reconnecting"
92
+ # establish_connection(uri)
93
+ # http = Thread.current[:connection]
94
+ # require 'ruby-debug'
95
+ # debugger
96
+ # req.body_stream = nil
97
+ #
98
+ # res = http.request(req, payload)
99
+ # display_log response_log(res)
100
+ # result res
101
+ # else
102
+ # display_log response_log(res)
103
+ # process_result res
104
+ # end
105
+ #
106
+ # rescue EOFError
107
+ # raise RestClient::ServerBrokeConnection
108
+ # rescue Timeout::Error
109
+ # raise RestClient::RequestTimeout
110
+ # end
111
+ # end
112
+
102
113
  end
@@ -23,7 +23,9 @@ module CouchRest
23
23
  end
24
24
 
25
25
  # Callbacks
26
+ define_callbacks :create
26
27
  define_callbacks :save
28
+ define_callbacks :update
27
29
  define_callbacks :destroy
28
30
 
29
31
  def initialize(keys={})
@@ -105,12 +107,62 @@ module CouchRest
105
107
  # for compatibility with old-school frameworks
106
108
  alias :new_record? :new_document?
107
109
 
110
+ # Trigger the callbacks (before, after, around)
111
+ # and create the document
112
+ # It's important to have a create callback since you can't check if a document
113
+ # was new after you saved it
114
+ #
115
+ # When creating a document, both the create and the save callbacks will be triggered.
116
+ def create(bulk = false)
117
+ caught = catch(:halt) do
118
+ _run_create_callbacks do
119
+ _run_save_callbacks do
120
+ create_without_callbacks(bulk)
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ # unlike save, create returns the newly created document
127
+ def create_without_callbacks(bulk =false)
128
+ raise ArgumentError, "a document requires a database to be created to (The document or the #{self.class} default database were not set)" unless database
129
+ set_unique_id if new_document? && self.respond_to?(:set_unique_id)
130
+ result = database.save_doc(self, bulk)
131
+ (result["ok"] == true) ? self : false
132
+ end
133
+
134
+ # Creates the document in the db. Raises an exception
135
+ # if the document is not created properly.
136
+ def create!
137
+ raise "#{self.inspect} failed to save" unless self.create
138
+ end
139
+
140
+ # Trigger the callbacks (before, after, around)
141
+ # only if the document isn't new
142
+ def update(bulk = false)
143
+ caught = catch(:halt) do
144
+ if self.new_document?
145
+ save(bulk)
146
+ else
147
+ _run_update_callbacks do
148
+ _run_save_callbacks do
149
+ save_without_callbacks(bulk)
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+
108
156
  # Trigger the callbacks (before, after, around)
109
157
  # and save the document
110
158
  def save(bulk = false)
111
159
  caught = catch(:halt) do
112
- _run_save_callbacks do
113
- save_without_callbacks(bulk)
160
+ if self.new_document?
161
+ _run_save_callbacks do
162
+ save_without_callbacks(bulk)
163
+ end
164
+ else
165
+ update(bulk)
114
166
  end
115
167
  end
116
168
  end
@@ -124,7 +176,7 @@ module CouchRest
124
176
  result["ok"] == true
125
177
  end
126
178
 
127
- # Saves the document to the db using create or update. Raises an exception
179
+ # Saves the document to the db using save. Raises an exception
128
180
  # if the document is not saved properly.
129
181
  def save!
130
182
  raise "#{self.inspect} failed to save" unless self.save
@@ -7,13 +7,22 @@ module CouchRest
7
7
  # attribute to define
8
8
  def initialize(name, type = nil, options = {})
9
9
  @name = name.to_s
10
- @type = type.nil? ? 'String' : type.to_s
10
+ parse_type(type)
11
11
  parse_options(options)
12
12
  self
13
13
  end
14
14
 
15
15
 
16
16
  private
17
+
18
+ def parse_type(type)
19
+ if type.nil?
20
+ @type = 'String'
21
+ else
22
+ @type = type.is_a?(Array) ? [type.first.to_s] : type.to_s
23
+ end
24
+ end
25
+
17
26
  def parse_options(options)
18
27
  return if options.empty?
19
28
  @validation_format = options.delete(:format) if options[:format]
data/lib/couchrest.rb CHANGED
@@ -27,7 +27,7 @@ require 'couchrest/monkeypatches'
27
27
 
28
28
  # = CouchDB, close to the metal
29
29
  module CouchRest
30
- VERSION = '0.13.1'
30
+ VERSION = '0.13.2'
31
31
 
32
32
  autoload :Server, 'couchrest/core/server'
33
33
  autoload :Database, 'couchrest/core/database'
@@ -10,6 +10,7 @@ class DummyModel < CouchRest::ExtendedDocument
10
10
  use_database TEST_SERVER.default_database
11
11
  raise "Default DB not set" if TEST_SERVER.default_database.nil?
12
12
  property :casted_attribute, :cast_as => 'WithCastedModelMixin'
13
+ property :keywords, :cast_as => ["String"]
13
14
  end
14
15
 
15
16
  describe CouchRest::CastedModel do
@@ -55,6 +56,18 @@ describe CouchRest::CastedModel do
55
56
  end
56
57
  end
57
58
 
59
+ describe "casted as an array of a different type" do
60
+ before(:each) do
61
+ @obj = DummyModel.new(:keywords => ['couch', 'sofa', 'relax', 'canapé'])
62
+ end
63
+
64
+ it "should cast the array propery" do
65
+ @obj.keywords.should be_an_instance_of(Array)
66
+ @obj.keywords.first.should == 'couch'
67
+ end
68
+
69
+ end
70
+
58
71
  describe "saved document with casted models" do
59
72
  before(:each) do
60
73
  @obj = DummyModel.new(:casted_attribute => {:name => 'whatever'})
@@ -10,12 +10,41 @@ describe "ExtendedDocument" do
10
10
  timestamps!
11
11
  end
12
12
 
13
+ class WithCallBacks < CouchRest::ExtendedDocument
14
+ use_database TEST_SERVER.default_database
15
+ property :name
16
+ property :run_before_save
17
+ property :run_after_save
18
+ property :run_before_create
19
+ property :run_after_create
20
+ property :run_before_update
21
+ property :run_after_update
22
+
23
+ save_callback :before do |object|
24
+ object.run_before_save = true
25
+ end
26
+ save_callback :after do |object|
27
+ object.run_after_save = true
28
+ end
29
+ create_callback :before do |object|
30
+ object.run_before_create = true
31
+ end
32
+ create_callback :after do |object|
33
+ object.run_after_create = true
34
+ end
35
+ update_callback :before do |object|
36
+ object.run_before_update = true
37
+ end
38
+ update_callback :after do |object|
39
+ object.run_after_update = true
40
+ end
41
+ end
42
+
13
43
  before(:each) do
14
44
  @obj = WithDefaultValues.new
15
45
  end
16
46
 
17
47
  describe "with default" do
18
-
19
48
  it "should have the default value set at initalization" do
20
49
  @obj.preset.should == {:right => 10, :top_align => false}
21
50
  end
@@ -28,7 +57,6 @@ describe "ExtendedDocument" do
28
57
  end
29
58
 
30
59
  describe "timestamping" do
31
-
32
60
  it "should define the updated_at and created_at getters and set the values" do
33
61
  @obj.save
34
62
  obj = WithDefaultValues.get(@obj.id)
@@ -36,12 +64,10 @@ describe "ExtendedDocument" do
36
64
  obj.created_at.should be_an_instance_of(Time)
37
65
  obj.updated_at.should be_an_instance_of(Time)
38
66
  obj.created_at.to_s.should == @obj.updated_at.to_s
39
- end
40
-
67
+ end
41
68
  end
42
69
 
43
70
  describe "saving and retrieving" do
44
-
45
71
  it "should work fine" do
46
72
  @obj.name = "should be easily saved and retrieved"
47
73
  @obj.save
@@ -57,7 +83,86 @@ describe "ExtendedDocument" do
57
83
  saved_obj = WithDefaultValues.get(@obj.id)
58
84
  saved_obj.set_by_proc.should be_an_instance_of(Time)
59
85
  end
60
-
61
86
  end
62
87
 
88
+ describe "callbacks" do
89
+
90
+ before(:each) do
91
+ @doc = WithCallBacks.new
92
+ end
93
+
94
+ describe "save" do
95
+ it "should not run the before filter before saving if the save failed" do
96
+ @doc.run_before_save.should be_nil
97
+ @doc.save.should be_true
98
+ @doc.run_before_save.should be_true
99
+ end
100
+ it "should not run the before filter before saving if the save failed" do
101
+ @doc.should_receive(:save).and_return(false)
102
+ @doc.run_before_save.should be_nil
103
+ @doc.save.should be_false
104
+ @doc.run_before_save.should be_nil
105
+ end
106
+ it "should run the after filter after saving" do
107
+ @doc.run_after_save.should be_nil
108
+ @doc.save.should be_true
109
+ @doc.run_after_save.should be_true
110
+ end
111
+ it "should not run the after filter before saving if the save failed" do
112
+ @doc.should_receive(:save).and_return(false)
113
+ @doc.run_after_save.should be_nil
114
+ @doc.save.should be_false
115
+ @doc.run_after_save.should be_nil
116
+ end
117
+ end
118
+ describe "create" do
119
+ it "should run the before save filter when creating" do
120
+ @doc.run_before_save.should be_nil
121
+ @doc.create.should_not be_nil
122
+ @doc.run_before_save.should be_true
123
+ end
124
+ it "should not run the before save filter when the object creation fails" do
125
+ pending "need to ask wycats about chainable callbacks" do
126
+ @doc.should_receive(:create_without_callbacks).and_return(false)
127
+ @doc.run_before_save.should be_nil
128
+ @doc.save
129
+ @doc.run_before_save.should be_nil
130
+ end
131
+ end
132
+ it "should run the before create filter" do
133
+ @doc.run_before_create.should be_nil
134
+ @doc.create.should_not be_nil
135
+ @doc.create
136
+ @doc.run_before_create.should be_true
137
+ end
138
+ it "should run the after create filter" do
139
+ @doc.run_after_create.should be_nil
140
+ @doc.create.should_not be_nil
141
+ @doc.create
142
+ @doc.run_after_create.should be_true
143
+ end
144
+ end
145
+ describe "update" do
146
+
147
+ before(:each) do
148
+ @doc.save
149
+ end
150
+ it "should run the before update filter when updating an existing document" do
151
+ @doc.run_before_update.should be_nil
152
+ @doc.update
153
+ @doc.run_before_update.should be_true
154
+ end
155
+ it "should run the after update filter when updating an existing document" do
156
+ @doc.run_after_update.should be_nil
157
+ @doc.update
158
+ @doc.run_after_update.should be_true
159
+ end
160
+ it "should run the before update filter when saving an existing document" do
161
+ @doc.run_before_update.should be_nil
162
+ @doc.save
163
+ @doc.run_before_update.should be_true
164
+ end
165
+
166
+ end
167
+ end
63
168
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mattetti-couchrest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.13.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - J. Chris Anderson
@@ -15,6 +15,7 @@ default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: json
18
+ type: :runtime
18
19
  version_requirement:
19
20
  version_requirements: !ruby/object:Gem::Requirement
20
21
  requirements:
@@ -24,6 +25,7 @@ dependencies:
24
25
  version:
25
26
  - !ruby/object:Gem::Dependency
26
27
  name: rest-client
28
+ type: :runtime
27
29
  version_requirement:
28
30
  version_requirements: !ruby/object:Gem::Requirement
29
31
  requirements:
@@ -33,6 +35,7 @@ dependencies:
33
35
  version:
34
36
  - !ruby/object:Gem::Dependency
35
37
  name: mime-types
38
+ type: :runtime
36
39
  version_requirement:
37
40
  version_requirements: !ruby/object:Gem::Requirement
38
41
  requirements: