couchrest 0.30 → 0.31

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/README.md CHANGED
@@ -12,14 +12,17 @@ Note: CouchRest only support CouchDB 0.9.0 or newer.
12
12
 
13
13
  ## Easy Install
14
14
 
15
- Easy Install is moving to RubyForge, heads up for the gem.
15
+ $ sudo gem install couchrest
16
+
17
+ Alternatively, you can install from Github:
18
+
19
+ $ gem sources -a http://gems.github.com (you only have to do this once)
20
+ $ sudo gem install mattetti-couchrest
16
21
 
17
22
  ### Relax, it's RESTful
18
23
 
19
- The core of Couchrest is Heroku’s excellent REST Client Ruby HTTP wrapper.
20
- REST Client takes all the nastyness of Net::HTTP and gives is a pretty face,
21
- while still giving you more control than Open-URI. I recommend it anytime
22
- you’re interfacing with a well-defined web service.
24
+ CouchRest rests on top of a HTTP abstraction layer using by default Heroku’s excellent REST Client Ruby HTTP wrapper.
25
+ Other adapters can be added to support more http libraries.
23
26
 
24
27
  ### Running the Specs
25
28
 
@@ -27,7 +30,7 @@ The most complete documentation is the spec/ directory. To validate your
27
30
  CouchRest install, from the project root directory run `rake`, or `autotest`
28
31
  (requires RSpec and optionally ZenTest for autotest support).
29
32
 
30
- ## Examples
33
+ ## Examples (CouchRest Core)
31
34
 
32
35
  Quick Start:
33
36
 
@@ -59,12 +62,50 @@ Creating and Querying Views:
59
62
  })
60
63
  puts @db.view('first/test')['rows'].inspect
61
64
 
62
- ## CouchRest::Model
63
65
 
64
- CouchRest::Model has been deprecated and replaced by CouchRest::ExtendedDocument
66
+ ## CouchRest::ExtendedDocument
67
+
68
+ CouchRest::ExtendedDocument is a DSL/ORM for CouchDB. Basically, ExtendedDocument seats on top of CouchRest Core to add the concept of Model.
69
+ ExtendedDocument offers a lot of the usual ORM tools such as optional yet defined schema, validation, callbacks, pagination, casting and much more.
70
+
71
+ ### Model example
72
+
73
+ Check spec/couchrest/more and spec/fixtures/more for more examples
74
+
75
+ class Article < CouchRest::ExtendedDocument
76
+ use_database DB
77
+ unique_id :slug
78
+
79
+ view_by :date, :descending => true
80
+ view_by :user_id, :date
81
+
82
+ view_by :tags,
83
+ :map =>
84
+ "function(doc) {
85
+ if (doc['couchrest-type'] == 'Article' && doc.tags) {
86
+ doc.tags.forEach(function(tag){
87
+ emit(tag, 1);
88
+ });
89
+ }
90
+ }",
91
+ :reduce =>
92
+ "function(keys, values, rereduce) {
93
+ return sum(values);
94
+ }"
95
+
96
+ property :date
97
+ property :slug, :read_only => true
98
+ property :title
99
+ property :tags, :cast_as => ['String']
65
100
 
101
+ timestamps!
66
102
 
67
- ## CouchRest::ExtendedDocument
103
+ save_callback :before, :generate_slug_from_title
104
+
105
+ def generate_slug_from_title
106
+ self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if new_document?
107
+ end
108
+ end
68
109
 
69
110
  ### Callbacks
70
111
 
@@ -97,9 +138,28 @@ If you want to cast an array of instances from a specific Class, use the trick s
97
138
 
98
139
  Pagination is available in any ExtendedDocument classes. Here are some usage examples:
99
140
 
100
- articles = Article.by_date :key => Date.today
101
- articles.paginate(:page => 1, :per_page => 3)
102
-
141
+ basic usage:
142
+
143
+ Article.all.paginate(:page => 1, :per_page => 5)
144
+
145
+ note: the above query will look like: `GET /db/_design/Article/_view/all?include_docs=true&skip=0&limit=5&reduce=false` and only fetch 5 documents.
146
+
147
+ Slightly more advance usage:
148
+
149
+ Article.by_name(:startkey => 'a', :endkey => {}).paginate(:page => 1, :per_page => 5)
150
+
151
+ note: the above query will look like: `GET /db/_design/Article/_view/by_name?startkey=%22a%22&limit=5&skip=0&endkey=%7B%7D&include_docs=true`
152
+ Basically, you can paginate through the articles starting by the letter a, 5 articles at a time.
153
+
154
+
155
+ Low level usage:
103
156
 
104
157
  Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
105
- :per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true)
158
+ :per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true)
159
+
160
+
161
+ ## Ruby on Rails
162
+
163
+ CouchRest is compatible with rails and can even be used a Rails plugin.
164
+ However, you might be interested in the CouchRest companion rails project:
165
+ [http://github.com/hpoydar/couchrest-rails](http://github.com/hpoydar/couchrest-rails)
@@ -1,3 +1,17 @@
1
+ == 0.31
2
+
3
+ * Major enhancements
4
+
5
+ * Created an abstraction HTTP layer to support different http adapters (Matt Aimonetti)
6
+ * Added ExtendedDocument.create({}) and #create!({}) so you don't have to do Model.new.create (Matt Aimonetti)
7
+
8
+ * Minor enhancements
9
+
10
+ * Added an init.rb file for easy usage as a Rails plugin (Aaron Quint)
11
+ * Bug fix: pagination shouldn't die on empty results (Arnaud Berthomier)
12
+ * Optimized ExtendedDocument.count to run about 3x faster (Matt Aimonetti)
13
+ * Added Float casting (Ryan Felton & Matt Aimonetti)
14
+
1
15
  == 0.30
2
16
 
3
17
  * Major enhancements
@@ -28,7 +28,7 @@ require 'couchrest/monkeypatches'
28
28
 
29
29
  # = CouchDB, close to the metal
30
30
  module CouchRest
31
- VERSION = '0.30' unless self.const_defined?("VERSION")
31
+ VERSION = '0.31' unless self.const_defined?("VERSION")
32
32
 
33
33
  autoload :Server, 'couchrest/core/server'
34
34
  autoload :Database, 'couchrest/core/database'
@@ -45,6 +45,7 @@ module CouchRest
45
45
  autoload :ExtendedDocument, 'couchrest/more/extended_document'
46
46
  autoload :CastedModel, 'couchrest/more/casted_model'
47
47
 
48
+ require File.join(File.dirname(__FILE__), 'couchrest', 'core', 'http_abstraction')
48
49
  require File.join(File.dirname(__FILE__), 'couchrest', 'mixins')
49
50
 
50
51
  # The CouchRest module methods handle the basic JSON serialization
@@ -118,9 +119,9 @@ module CouchRest
118
119
  }
119
120
  end
120
121
 
121
- # set proxy for RestClient to use
122
+ # set proxy to use
122
123
  def proxy url
123
- RestClient.proxy = url
124
+ HttpAbstraction.proxy = url
124
125
  end
125
126
 
126
127
  # ensure that a database exists
@@ -141,7 +142,7 @@ module CouchRest
141
142
  def put(uri, doc = nil)
142
143
  payload = doc.to_json if doc
143
144
  begin
144
- JSON.parse(RestClient.put(uri, payload))
145
+ JSON.parse(HttpAbstraction.put(uri, payload))
145
146
  rescue Exception => e
146
147
  if $DEBUG
147
148
  raise "Error while sending a PUT request #{uri}\npayload: #{payload.inspect}\n#{e}"
@@ -153,7 +154,7 @@ module CouchRest
153
154
 
154
155
  def get(uri)
155
156
  begin
156
- JSON.parse(RestClient.get(uri), :max_nesting => false)
157
+ JSON.parse(HttpAbstraction.get(uri), :max_nesting => false)
157
158
  rescue => e
158
159
  if $DEBUG
159
160
  raise "Error while sending a GET request #{uri}\n: #{e}"
@@ -166,7 +167,7 @@ module CouchRest
166
167
  def post uri, doc = nil
167
168
  payload = doc.to_json if doc
168
169
  begin
169
- JSON.parse(RestClient.post(uri, payload))
170
+ JSON.parse(HttpAbstraction.post(uri, payload))
170
171
  rescue Exception => e
171
172
  if $DEBUG
172
173
  raise "Error while sending a POST request #{uri}\npayload: #{payload.inspect}\n#{e}"
@@ -177,11 +178,11 @@ module CouchRest
177
178
  end
178
179
 
179
180
  def delete uri
180
- JSON.parse(RestClient.delete(uri))
181
+ JSON.parse(HttpAbstraction.delete(uri))
181
182
  end
182
183
 
183
184
  def copy uri, destination
184
- JSON.parse(RestClient.copy(uri, {'Destination' => destination}))
185
+ JSON.parse(HttpAbstraction.copy(uri, {'Destination' => destination}))
185
186
  end
186
187
 
187
188
  def paramify_url url, params = {}
@@ -0,0 +1,35 @@
1
+ module RestClientAdapter
2
+
3
+ module API
4
+ def proxy=(url)
5
+ RestClient.proxy = url
6
+ end
7
+
8
+ def proxy
9
+ RestClient.proxy
10
+ end
11
+
12
+ def get(uri, headers={})
13
+ RestClient.get(uri, headers)
14
+ end
15
+
16
+ def post(uri, payload, headers={})
17
+ RestClient.post(uri, payload, headers)
18
+ end
19
+
20
+ def put(uri, payload, headers={})
21
+ RestClient.put(uri, payload, headers)
22
+ end
23
+
24
+ def delete(uri, headers={})
25
+ RestClient.delete(uri, headers)
26
+ end
27
+
28
+ def copy(uri, headers)
29
+ RestClient::Request.execute( :method => :copy,
30
+ :url => uri,
31
+ :headers => headers)
32
+ end
33
+ end
34
+
35
+ end
@@ -58,7 +58,7 @@ module CouchRest
58
58
  keys = params.delete(:keys)
59
59
  funcs = funcs.merge({:keys => keys}) if keys
60
60
  url = CouchRest.paramify_url "#{@root}/_temp_view", params
61
- JSON.parse(RestClient.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
61
+ JSON.parse(HttpAbstraction.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
62
62
  end
63
63
 
64
64
  # backwards compatibility is a plus
@@ -100,11 +100,8 @@ module CouchRest
100
100
 
101
101
  # GET an attachment directly from CouchDB
102
102
  def fetch_attachment(doc, name)
103
- # slug = escape_docid(docid)
104
- # name = CGI.escape(name)
105
103
  uri = url_for_attachment(doc, name)
106
- RestClient.get uri
107
- # "#{@uri}/#{slug}/#{name}"
104
+ HttpAbstraction.get uri
108
105
  end
109
106
 
110
107
  # PUT an attachment directly to CouchDB
@@ -112,14 +109,14 @@ module CouchRest
112
109
  docid = escape_docid(doc['_id'])
113
110
  name = CGI.escape(name)
114
111
  uri = url_for_attachment(doc, name)
115
- JSON.parse(RestClient.put(uri, file, options))
112
+ JSON.parse(HttpAbstraction.put(uri, file, options))
116
113
  end
117
114
 
118
115
  # DELETE an attachment directly from CouchDB
119
116
  def delete_attachment doc, name
120
117
  uri = url_for_attachment(doc, name)
121
118
  # this needs a rev
122
- JSON.parse(RestClient.delete(uri))
119
+ JSON.parse(HttpAbstraction.delete(uri))
123
120
  end
124
121
 
125
122
  # Save a document to CouchDB. This will use the <tt>_id</tt> field from
@@ -146,7 +143,7 @@ module CouchRest
146
143
  slug = escape_docid(doc['_id'])
147
144
  begin
148
145
  CouchRest.put "#{@root}/#{slug}", doc
149
- rescue RestClient::ResourceNotFound
146
+ rescue HttpAbstraction::ResourceNotFound
150
147
  p "resource not found when saving even tho an id was passed"
151
148
  slug = doc['_id'] = @server.next_uuid
152
149
  CouchRest.put "#{@root}/#{slug}", doc
@@ -252,7 +249,7 @@ module CouchRest
252
249
  def recreate!
253
250
  delete!
254
251
  create!
255
- rescue RestClient::ResourceNotFound
252
+ rescue HttpAbstraction::ResourceNotFound
256
253
  ensure
257
254
  create!
258
255
  end
@@ -3,10 +3,6 @@ require 'delegate'
3
3
  module CouchRest
4
4
  class Document < Response
5
5
  include CouchRest::Mixins::Attachments
6
-
7
- # def self.inherited(subklass)
8
- # subklass.send(:extlib_inheritable_accessor, :database)
9
- # end
10
6
 
11
7
  extlib_inheritable_accessor :database
12
8
  attr_accessor :database
@@ -0,0 +1,48 @@
1
+ require 'couchrest/core/adapters/restclient'
2
+
3
+ # Abstraction layet for HTTP communications.
4
+ #
5
+ # By defining a basic API that CouchRest is relying on,
6
+ # it allows for easy experimentations and implementations of various libraries.
7
+ #
8
+ # Most of the API is based on the RestClient API that was used in the early version of CouchRest.
9
+ #
10
+ module HttpAbstraction
11
+
12
+ # here is the list of exception expected by CouchRest
13
+ # please convert the underlying errors in this set of known
14
+ # exceptions.
15
+ class ResourceNotFound < StandardError; end
16
+ class RequestFailed < StandardError; end
17
+ class RequestTimeout < StandardError; end
18
+ class ServerBrokeConnection < StandardError; end
19
+ class Conflict < StandardError; end
20
+
21
+
22
+ # # Here is the API you need to implement if you want to write a new adapter
23
+ # # See adapters/restclient.rb for more information.
24
+ #
25
+ # def self.proxy=(url)
26
+ # end
27
+ #
28
+ # def self.proxy
29
+ # end
30
+ #
31
+ # def self.get(uri, headers=nil)
32
+ # end
33
+ #
34
+ # def self.post(uri, payload, headers=nil)
35
+ # end
36
+ #
37
+ # def self.put(uri, payload, headers=nil)
38
+ # end
39
+ #
40
+ # def self.delete(uri, headers=nil)
41
+ # end
42
+ #
43
+ # def self.copy(uri, headers)
44
+ # end
45
+
46
+ end
47
+
48
+ HttpAbstraction.extend(RestClientAdapter::API)
@@ -209,12 +209,14 @@ module CouchRest
209
209
 
210
210
  def remember_where_we_left_off(results, page)
211
211
  last_row = results['rows'].last
212
- @last_key = last_row['key']
213
- @last_docid = last_row['id']
212
+ if last_row
213
+ @last_key = last_row['key']
214
+ @last_docid = last_row['id']
215
+ end
214
216
  @last_page = page
215
217
  end
216
218
  end
217
219
 
218
220
  end
219
221
  end
220
- end
222
+ end
@@ -37,9 +37,6 @@ module CouchRest
37
37
  if (doc['couchrest-type'] == '#{self.to_s}') {
38
38
  emit(null,1);
39
39
  }
40
- }",
41
- 'reduce' => "function(keys, values) {
42
- return sum(values);
43
40
  }"
44
41
  }
45
42
  }
@@ -19,9 +19,7 @@ module CouchRest
19
19
  # equal to the name of the current class. Takes the standard set of
20
20
  # CouchRest::Database#view options
21
21
  def count(opts = {}, &block)
22
- result = all({:reduce => true}.merge(opts), &block)['rows']
23
- return 0 if result.empty?
24
- result.first['value']
22
+ all({:raw => true, :limit => 0}.merge(opts), &block)['total_rows']
25
23
  end
26
24
 
27
25
  # Load the first document that have the "couchrest-type" field equal to
@@ -56,7 +56,6 @@ module CouchRest
56
56
  def cast_keys
57
57
  return unless self.class.properties
58
58
  self.class.properties.each do |property|
59
-
60
59
  next unless property.casted
61
60
  key = self.has_key?(property.name) ? property.name : property.name.to_sym
62
61
  # Don't cast the property unless it has a value
@@ -75,6 +74,9 @@ module CouchRest
75
74
  self[property.name] = if ((property.init_method == 'new') && target == 'Time')
76
75
  # Using custom time parsing method because Ruby's default method is toooo slow
77
76
  self[key].is_a?(String) ? Time.mktime_with_offset(self[key].dup) : self[key]
77
+ # Float instances don't get initialized with #new
78
+ elsif ((property.init_method == 'new') && target == 'Float')
79
+ cast_float(self[key])
78
80
  else
79
81
  # Let people use :send as a Time parse arg
80
82
  klass = ::CouchRest.constantize(target)
@@ -84,6 +86,15 @@ module CouchRest
84
86
  end
85
87
 
86
88
  end
89
+
90
+ def cast_float(value)
91
+ begin
92
+ Float(value)
93
+ rescue
94
+ value
95
+ end
96
+ end
97
+
87
98
  end
88
99
 
89
100
  module ClassMethods
@@ -72,7 +72,7 @@ module CouchRest
72
72
  #
73
73
  # To understand the capabilities of this view system more completely,
74
74
  # it is recommended that you read the RSpec file at
75
- # <tt>spec/core/model_spec.rb</tt>.
75
+ # <tt>spec/couchrest/more/extended_doc_spec.rb</tt>.
76
76
 
77
77
  def view_by(*keys)
78
78
  opts = keys.pop if keys.last.is_a?(Hash)
@@ -124,14 +124,6 @@ module CouchRest
124
124
  # potentially large indexes.
125
125
  def cleanup_design_docs!(db = database)
126
126
  save_design_doc_on(db)
127
- # db.refresh_design_doc
128
- # db.save_design_doc
129
- # design_doc = model_design_doc(db)
130
- # if design_doc
131
- # db.delete_doc(design_doc)
132
- # else
133
- # false
134
- # end
135
127
  end
136
128
 
137
129
  private
@@ -162,7 +154,7 @@ module CouchRest
162
154
  begin
163
155
  design_doc.view_on(db, view_name, opts, &block)
164
156
  # the design doc may not have been saved yet on this database
165
- rescue RestClient::ResourceNotFound => e
157
+ rescue HttpAbstraction::ResourceNotFound => e
166
158
  if retryable
167
159
  save_design_doc_on(db)
168
160
  retryable = false
@@ -51,63 +51,63 @@ if RUBY_VERSION.to_f < 1.9
51
51
  end
52
52
  end
53
53
 
54
- module RestClient
55
- def self.copy(url, headers={})
56
- Request.execute(:method => :copy,
57
- :url => url,
58
- :headers => headers)
59
- end
60
-
61
- # class Request
62
- #
63
- # def establish_connection(uri)
64
- # Thread.current[:connection].finish if (Thread.current[:connection] && Thread.current[:connection].started?)
65
- # p net_http_class
66
- # net = net_http_class.new(uri.host, uri.port)
67
- # net.use_ssl = uri.is_a?(URI::HTTPS)
68
- # net.verify_mode = OpenSSL::SSL::VERIFY_NONE
69
- # Thread.current[:connection] = net
70
- # Thread.current[:connection].start
71
- # Thread.current[:connection]
72
- # end
73
- #
74
- # def transmit(uri, req, payload)
75
- # setup_credentials(req)
76
- #
77
- # Thread.current[:host] ||= uri.host
78
- # Thread.current[:port] ||= uri.port
79
- #
80
- # if (Thread.current[:connection].nil? || (Thread.current[:host] != uri.host))
81
- # p "establishing a connection"
82
- # establish_connection(uri)
83
- # end
54
+ # module RestClient
55
+ # # def self.copy(url, headers={})
56
+ # # Request.execute(:method => :copy,
57
+ # # :url => url,
58
+ # # :headers => headers)
59
+ # # end
84
60
  #
85
- # display_log request_log
86
- # http = Thread.current[:connection]
87
- # http.read_timeout = @timeout if @timeout
88
- #
89
- # begin
90
- # res = http.request(req, payload)
91
- # rescue
92
- # p "Net::HTTP connection failed, reconnecting"
93
- # establish_connection(uri)
94
- # http = Thread.current[:connection]
95
- # require 'ruby-debug'
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
-
113
- end
61
+ # # class Request
62
+ # #
63
+ # # def establish_connection(uri)
64
+ # # Thread.current[:connection].finish if (Thread.current[:connection] && Thread.current[:connection].started?)
65
+ # # p net_http_class
66
+ # # net = net_http_class.new(uri.host, uri.port)
67
+ # # net.use_ssl = uri.is_a?(URI::HTTPS)
68
+ # # net.verify_mode = OpenSSL::SSL::VERIFY_NONE
69
+ # # Thread.current[:connection] = net
70
+ # # Thread.current[:connection].start
71
+ # # Thread.current[:connection]
72
+ # # end
73
+ # #
74
+ # # def transmit(uri, req, payload)
75
+ # # setup_credentials(req)
76
+ # #
77
+ # # Thread.current[:host] ||= uri.host
78
+ # # Thread.current[:port] ||= uri.port
79
+ # #
80
+ # # if (Thread.current[:connection].nil? || (Thread.current[:host] != uri.host))
81
+ # # p "establishing a connection"
82
+ # # establish_connection(uri)
83
+ # # end
84
+ # #
85
+ # # display_log request_log
86
+ # # http = Thread.current[:connection]
87
+ # # http.read_timeout = @timeout if @timeout
88
+ # #
89
+ # # begin
90
+ # # res = http.request(req, payload)
91
+ # # rescue
92
+ # # p "Net::HTTP connection failed, reconnecting"
93
+ # # establish_connection(uri)
94
+ # # http = Thread.current[:connection]
95
+ # # require 'ruby-debug'
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
+ #
113
+ # end
@@ -52,8 +52,25 @@ module CouchRest
52
52
  end
53
53
  end
54
54
 
55
-
55
+ # Defines an instance and save it directly to the database
56
+ #
57
+ # ==== Returns
58
+ # returns the reloaded document
59
+ def self.create(options)
60
+ instance = new(options)
61
+ instance.create
62
+ instance
63
+ end
56
64
 
65
+ # Defines an instance and save it directly to the database
66
+ #
67
+ # ==== Returns
68
+ # returns the reloaded document or raises an exception
69
+ def self.create!(options)
70
+ instance = new(options)
71
+ instance.create!
72
+ instance
73
+ end
57
74
 
58
75
  # Automatically set <tt>updated_at</tt> and <tt>created_at</tt> fields
59
76
  # on the document whenever saving occurs. CouchRest uses a pretty
@@ -191,7 +191,7 @@ describe CouchRest do
191
191
  describe "using a proxy for RestClient connections" do
192
192
  it "should set proxy url for RestClient" do
193
193
  CouchRest.proxy 'http://localhost:8888/'
194
- proxy_uri = URI.parse(RestClient.proxy)
194
+ proxy_uri = URI.parse(HttpAbstraction.proxy)
195
195
  proxy_uri.host.should eql( 'localhost' )
196
196
  proxy_uri.port.should eql( 8888 )
197
197
  CouchRest.proxy nil
@@ -690,7 +690,7 @@ describe CouchRest::Database do
690
690
 
691
691
  it "should recreate a db even tho it doesn't exist" do
692
692
  @cr.databases.should_not include(@db2.name)
693
- @db2.recreate!
693
+ begin @db2.recreate! rescue nil end
694
694
  @cr.databases.should include(@db2.name)
695
695
  end
696
696
 
@@ -97,6 +97,22 @@ describe "ExtendedDocument" do
97
97
  end
98
98
  end
99
99
 
100
+ describe "creating a new document" do
101
+ it "should instantialize and save a document" do
102
+ article = Article.create(:title => 'my test')
103
+ article.title.should == 'my test'
104
+ article.should_not be_new_document
105
+ end
106
+
107
+ it "should trigger the create callbacks" do
108
+ doc = WithCallBacks.create(:name => 'my other test')
109
+ doc.run_before_create.should be_true
110
+ doc.run_after_create.should be_true
111
+ doc.run_before_save.should be_true
112
+ doc.run_after_save.should be_true
113
+ end
114
+ end
115
+
100
116
  describe "update attributes without saving" do
101
117
  before(:each) do
102
118
  a = Article.get "big-bad-danger" rescue nil
@@ -121,7 +121,7 @@ describe "ExtendedDocument views" do
121
121
  describe "a model class not tied to a database" do
122
122
  before(:all) do
123
123
  reset_test_db!
124
- @db = DB
124
+ @db = DB
125
125
  %w{aaa bbb ddd eee}.each do |title|
126
126
  u = Unattached.new(:title => title)
127
127
  u.database = @db
@@ -133,14 +133,15 @@ describe "ExtendedDocument views" do
133
133
  lambda{Unattached.all}.should raise_error
134
134
  end
135
135
  it "should query all" do
136
- rs = Unattached.all :database=>@db
136
+ Unattached.cleanup_design_docs!(@db)
137
+ rs = Unattached.all :database => @db
137
138
  rs.length.should == 4
138
139
  end
139
140
  it "should barf on query if no database given" do
140
141
  lambda{Unattached.view :by_title}.should raise_error
141
142
  end
142
143
  it "should make the design doc upon first query" do
143
- Unattached.by_title :database=>@db
144
+ Unattached.by_title :database => @db
144
145
  doc = Unattached.design_doc
145
146
  doc['views']['all']['map'].should include('Unattached')
146
147
  end
@@ -157,7 +158,7 @@ describe "ExtendedDocument views" do
157
158
  things = []
158
159
  Unattached.view(:by_title, :database=>@db) do |thing|
159
160
  things << thing
160
- end
161
+ end
161
162
  things[0]["doc"]["title"].should =='aaa'
162
163
  end
163
164
  it "should yield with by_key method" do
@@ -141,6 +141,29 @@ describe "ExtendedDocument properties" do
141
141
  @event['occurs_at'].should be_an_instance_of(Time)
142
142
  end
143
143
  end
144
+
145
+ describe "casting to Float object" do
146
+ class RootBeerFloat < CouchRest::ExtendedDocument
147
+ use_database DB
148
+ property :price, :cast_as => 'Float'
149
+ end
150
+
151
+ it "should convert a string into a float if casted as so" do
152
+ RootBeerFloat.new(:price => '12.50').price.should == 12.50
153
+ RootBeerFloat.new(:price => '9').price.should == 9.0
154
+ RootBeerFloat.new(:price => '-9').price.should == -9.0
155
+ end
156
+
157
+ it "should not convert a string if it's not a string that can be cast as a float" do
158
+ RootBeerFloat.new(:price => 'test').price.should == 'test'
159
+ end
160
+
161
+ it "should work fine when a float is being passed" do
162
+ RootBeerFloat.new(:price => 9.99).price.should == 9.99
163
+ end
164
+
165
+ end
166
+
144
167
  end
145
168
 
146
169
  end
@@ -20,7 +20,7 @@ class Basic < CouchRest::ExtendedDocument
20
20
  end
21
21
 
22
22
  def reset_test_db!
23
- DB.recreate! rescue nil
23
+ DB.recreate! rescue nil
24
24
  DB
25
25
  end
26
26
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couchrest
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.30"
4
+ version: "0.31"
5
5
  platform: ruby
6
6
  authors:
7
7
  - J. Chris Anderson
@@ -62,9 +62,11 @@ files:
62
62
  - examples/word_count/word_count_views.rb
63
63
  - lib/couchrest/commands/generate.rb
64
64
  - lib/couchrest/commands/push.rb
65
+ - lib/couchrest/core/adapters/restclient.rb
65
66
  - lib/couchrest/core/database.rb
66
67
  - lib/couchrest/core/design.rb
67
68
  - lib/couchrest/core/document.rb
69
+ - lib/couchrest/core/http_abstraction.rb
68
70
  - lib/couchrest/core/response.rb
69
71
  - lib/couchrest/core/server.rb
70
72
  - lib/couchrest/core/view.rb