couchrest 1.0.0.beta2 → 1.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'rake'
2
2
  require "rake/rdoctask"
3
- require File.join(File.expand_path(File.dirname(__FILE__)),'lib','couchrest')
3
+
4
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
5
+ require 'couchrest'
4
6
 
5
7
  begin
6
8
  require 'spec/rake/spectask'
@@ -25,7 +27,7 @@ begin
25
27
  gemspec.files = %w( LICENSE README.md Rakefile THANKS.md history.txt couchrest.gemspec) + Dir["{examples,lib,spec,utils}/**/*"] - Dir["spec/tmp"]
26
28
  gemspec.has_rdoc = true
27
29
  gemspec.add_dependency("rest-client", ">= 1.5.1")
28
- gemspec.add_dependency("json", "<= 1.4.2")
30
+ gemspec.add_dependency("json", "= 1.2.4") # json 1.4.3 has stack overflow issues!
29
31
  # gemspec.add_dependency("couchrest_extended_document", ">= 1.0.0")
30
32
  gemspec.version = CouchRest::VERSION
31
33
  gemspec.date = "2010-07-01"
@@ -65,4 +67,4 @@ module Rake
65
67
  end
66
68
 
67
69
  Rake.remove_task("github:release")
68
- Rake.remove_task("release")
70
+ Rake.remove_task("release")
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{couchrest}
8
- s.version = "1.0.0.beta2"
8
+ s.version = "1.0.0.beta3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["J. Chris Anderson", "Matt Aimonetti", "Marcos Tapajos", "Will Leinweber"]
12
- s.date = %q{2010-07-01}
12
+ s.date = %q{2010-07-25}
13
13
  s.description = %q{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.}
14
14
  s.email = %q{jchris@apache.org}
15
15
  s.extra_rdoc_files = [
@@ -49,7 +49,7 @@ Gem::Specification.new do |s|
49
49
  "lib/couchrest/response.rb",
50
50
  "lib/couchrest/rest_api.rb",
51
51
  "lib/couchrest/server.rb",
52
- "lib/couchrest/support/class.rb",
52
+ "lib/couchrest/support/inheritable_attributes.rb",
53
53
  "spec/couchrest/couchrest_spec.rb",
54
54
  "spec/couchrest/database_spec.rb",
55
55
  "spec/couchrest/design_spec.rb",
@@ -95,14 +95,14 @@ Gem::Specification.new do |s|
95
95
 
96
96
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
97
97
  s.add_runtime_dependency(%q<rest-client>, [">= 1.5.1"])
98
- s.add_runtime_dependency(%q<json>, ["<= 1.4.2"])
98
+ s.add_runtime_dependency(%q<json>, ["= 1.2.4"])
99
99
  else
100
100
  s.add_dependency(%q<rest-client>, [">= 1.5.1"])
101
- s.add_dependency(%q<json>, ["<= 1.4.2"])
101
+ s.add_dependency(%q<json>, ["= 1.2.4"])
102
102
  end
103
103
  else
104
104
  s.add_dependency(%q<rest-client>, [">= 1.5.1"])
105
- s.add_dependency(%q<json>, ["<= 1.4.2"])
105
+ s.add_dependency(%q<json>, ["= 1.2.4"])
106
106
  end
107
107
  end
108
108
 
@@ -2,8 +2,12 @@
2
2
 
3
3
  * Major enhancements
4
4
  * Add create_target option to Database#replicate_to and #replicate_from. http://github.com/couchrest/couchrest/issues/#issue/26 (Alexander Uvarov)
5
+ * Removing unused core extensions and moving extlib_inhertiable_* methods to use couchrest_inheritable_*
6
+ to avoid conflicts with Rails. (Geoff Buesing)
5
7
 
6
8
  * Minor enhancements
9
+ * Added Document#id= support (issue detected by Rory Franklin with RSpec model stubs)
10
+ * Fixing issues with CouchDB 1.0 and RestClient
7
11
 
8
12
  == 1.0.0.beta
9
13
 
@@ -13,9 +13,11 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require 'rubygems'
16
- gem 'json', "<= 1.4.2"
17
- require 'json'
18
16
  gem 'rest-client', ">= 1.5.1"
17
+ unless Kernel.const_defined?("JSON")
18
+ gem 'json', '1.2.4'
19
+ require 'json'
20
+ end
19
21
  require 'rest_client'
20
22
 
21
23
  # Not sure why this is required, so removed until a reason is found!
@@ -25,10 +27,11 @@ $:.unshift File.dirname(__FILE__) unless
25
27
 
26
28
  require 'couchrest/monkeypatches'
27
29
  require 'couchrest/rest_api'
30
+ require 'couchrest/support/inheritable_attributes'
28
31
 
29
32
  # = CouchDB, close to the metal
30
33
  module CouchRest
31
- VERSION = '1.0.0.beta2'
34
+ VERSION = '1.0.0.beta3'
32
35
 
33
36
  autoload :Server, 'couchrest/server'
34
37
  autoload :Database, 'couchrest/database'
@@ -134,4 +137,4 @@ class CouchRest::ExtendedDocument < CouchRest::Document
134
137
  raise "ExtendedDocument is no longer included in CouchRest base driver, see couchrest_extended_document gem"
135
138
  end
136
139
 
137
- end
140
+ end
@@ -65,7 +65,7 @@ module CouchRest
65
65
  keys = params.delete(:keys)
66
66
  funcs = funcs.merge({:keys => keys}) if keys
67
67
  url = CouchRest.paramify_url "#{@root}/_temp_view", params
68
- JSON.parse(RestClient.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
68
+ JSON.parse(RestClient.post(url, funcs.to_json, CouchRest.default_headers))
69
69
  end
70
70
 
71
71
  # backwards compatibility is a plus
@@ -108,14 +108,14 @@ module CouchRest
108
108
  # GET an attachment directly from CouchDB
109
109
  def fetch_attachment(doc, name)
110
110
  uri = url_for_attachment(doc, name)
111
- RestClient.get uri
111
+ RestClient.get uri, CouchRest.default_headers
112
112
  end
113
113
 
114
114
  # PUT an attachment directly to CouchDB
115
115
  def put_attachment(doc, name, file, options = {})
116
116
  docid = escape_docid(doc['_id'])
117
117
  uri = url_for_attachment(doc, name)
118
- JSON.parse(RestClient.put(uri, file, options))
118
+ JSON.parse(RestClient.put(uri, file, CouchRest.default_headers.merge(options)))
119
119
  end
120
120
 
121
121
  # DELETE an attachment directly from CouchDB
@@ -123,13 +123,13 @@ module CouchRest
123
123
  uri = url_for_attachment(doc, name)
124
124
  # this needs a rev
125
125
  begin
126
- JSON.parse(RestClient.delete(uri))
126
+ CouchRest.delete(uri)
127
127
  rescue Exception => error
128
128
  if force
129
129
  # get over a 409
130
130
  doc = get(doc['_id'])
131
131
  uri = url_for_attachment(doc, name)
132
- JSON.parse(RestClient.delete(uri))
132
+ CouchRest.delete(uri)
133
133
  else
134
134
  error
135
135
  end
@@ -18,7 +18,7 @@ module CouchRest
18
18
  doc_keys = keys.collect{|k|"doc['#{k}']"} # this is where :require => 'doc.x == true' would show up
19
19
  key_emit = doc_keys.length == 1 ? "#{doc_keys.first}" : "[#{doc_keys.join(', ')}]"
20
20
  guards = opts.delete(:guards) || []
21
- guards.concat doc_keys
21
+ guards += doc_keys.map{|k| "(#{k} != null)"}
22
22
  map_function = <<-JAVASCRIPT
23
23
  function(doc) {
24
24
  if (#{guards.join(' && ')}) {
@@ -3,8 +3,9 @@ require 'delegate'
3
3
  module CouchRest
4
4
  class Document < Response
5
5
  include CouchRest::Attachments
6
+ extend CouchRest::InheritableAttributes
6
7
 
7
- extlib_inheritable_accessor :database
8
+ couchrest_inheritable_accessor :database
8
9
  attr_accessor :database
9
10
 
10
11
  # override the CouchRest::Model-wide default_database
@@ -17,6 +18,10 @@ module CouchRest
17
18
  def id
18
19
  self['_id']
19
20
  end
21
+
22
+ def id=(id)
23
+ self['_id'] = id
24
+ end
20
25
 
21
26
  def rev
22
27
  self['_rev']
@@ -1,4 +1,3 @@
1
- require File.join(File.dirname(__FILE__), 'support', 'class')
2
1
  require 'timeout'
3
2
 
4
3
  # This file must be loaded after the JSON gem and any other library that beats up the Time class.
@@ -1,9 +1,16 @@
1
1
  module RestAPI
2
2
 
3
+ def default_headers
4
+ {
5
+ :content_type => :json,
6
+ :accept => :json
7
+ }
8
+ end
9
+
3
10
  def put(uri, doc = nil)
4
11
  payload = doc.to_json if doc
5
12
  begin
6
- JSON.parse(RestClient.put(uri, payload))
13
+ JSON.parse(RestClient.put(uri, payload, default_headers))
7
14
  rescue Exception => e
8
15
  if $DEBUG
9
16
  raise "Error while sending a PUT request #{uri}\npayload: #{payload.inspect}\n#{e}"
@@ -15,7 +22,7 @@ module RestAPI
15
22
 
16
23
  def get(uri)
17
24
  begin
18
- JSON.parse(RestClient.get(uri), :max_nesting => false)
25
+ JSON.parse(RestClient.get(uri, default_headers), :max_nesting => false)
19
26
  rescue => e
20
27
  if $DEBUG
21
28
  raise "Error while sending a GET request #{uri}\n: #{e}"
@@ -28,7 +35,7 @@ module RestAPI
28
35
  def post(uri, doc = nil)
29
36
  payload = doc.to_json if doc
30
37
  begin
31
- JSON.parse(RestClient.post(uri, payload))
38
+ JSON.parse(RestClient.post(uri, payload, default_headers))
32
39
  rescue Exception => e
33
40
  if $DEBUG
34
41
  raise "Error while sending a POST request #{uri}\npayload: #{payload.inspect}\n#{e}"
@@ -39,13 +46,13 @@ module RestAPI
39
46
  end
40
47
 
41
48
  def delete(uri)
42
- JSON.parse(RestClient.delete(uri))
49
+ JSON.parse(RestClient.delete(uri, default_headers))
43
50
  end
44
51
 
45
52
  def copy(uri, destination)
46
53
  JSON.parse(RestClient::Request.execute( :method => :copy,
47
54
  :url => uri,
48
- :headers => {'Destination' => destination}
55
+ :headers => default_headers.merge('Destination' => destination)
49
56
  ).to_s)
50
57
  end
51
58
 
@@ -73,6 +73,8 @@ module CouchRest
73
73
  # Restart the CouchDB instance
74
74
  def restart!
75
75
  CouchRest.post "#{@uri}/_restart"
76
+ rescue RestClient::ServerBrokeConnection
77
+ # Shouldn't really happen, but does in CouchDB 1.0.0
76
78
  end
77
79
 
78
80
  # Retrive an unused UUID from CouchDB. Server instances manage caching a list of unused UUIDs.
@@ -0,0 +1,107 @@
1
+ # Copyright (c) 2006-2009 David Heinemeier Hansson
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ #
22
+ # Extracted From
23
+ # http://github.com/rails/rails/commit/971e2438d98326c994ec6d3ef8e37b7e868ed6e2
24
+
25
+ module CouchRest
26
+ module InheritableAttributes
27
+
28
+ # Defines class-level inheritable attribute reader. Attributes are available to subclasses,
29
+ # each subclass has a copy of parent's attribute.
30
+ #
31
+ # @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
32
+ # @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
33
+ #
34
+ # @api public
35
+ #
36
+ # @todo Do we want to block instance_reader via :instance_reader => false
37
+ # @todo It would be preferable that we do something with a Hash passed in
38
+ # (error out or do the same as other methods above) instead of silently
39
+ # moving on). In particular, this makes the return value of this function
40
+ # less useful.
41
+ def couchrest_inheritable_reader(*ivars)
42
+ instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
43
+
44
+ ivars.each do |ivar|
45
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
46
+ def self.#{ivar}
47
+ return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
48
+ ivar = superclass.#{ivar}
49
+ return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
50
+ @#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) ? ivar.dup : ivar
51
+ end
52
+ RUBY
53
+ unless instance_reader == false
54
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
55
+ def #{ivar}
56
+ self.class.#{ivar}
57
+ end
58
+ RUBY
59
+ end
60
+ end
61
+ end
62
+
63
+ # Defines class-level inheritable attribute writer. Attributes are available to subclasses,
64
+ # each subclass has a copy of parent's attribute.
65
+ #
66
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
67
+ # define inheritable writer for.
68
+ # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
69
+ # @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
70
+ #
71
+ # @api public
72
+ #
73
+ # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
74
+ # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
75
+ def couchrest_inheritable_writer(*ivars)
76
+ instance_writer = ivars.pop[:writer] if ivars.last.is_a?(Hash)
77
+ ivars.each do |ivar|
78
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
79
+ def self.#{ivar}=(obj)
80
+ @#{ivar} = obj
81
+ end
82
+ RUBY
83
+ unless instance_writer == false
84
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
85
+ def #{ivar}=(obj) self.class.#{ivar} = obj end
86
+ RUBY
87
+ end
88
+
89
+ self.send("#{ivar}=", yield) if block_given?
90
+ end
91
+ end
92
+
93
+ # Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
94
+ # each subclass has a copy of parent's attribute.
95
+ #
96
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
97
+ # define inheritable accessor for.
98
+ # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
99
+ # @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
100
+ #
101
+ # @api public
102
+ def couchrest_inheritable_accessor(*syms, &block)
103
+ couchrest_inheritable_reader(*syms)
104
+ couchrest_inheritable_writer(*syms, &block)
105
+ end
106
+ end
107
+ end
@@ -28,6 +28,13 @@ describe CouchRest do
28
28
 
29
29
  it "should restart" do
30
30
  @cr.restart!
31
+ begin
32
+ @cr.info
33
+ rescue
34
+ # Give the couchdb time to restart
35
+ sleep 0.2
36
+ retry
37
+ end
31
38
  end
32
39
 
33
40
  it "should provide one-time access to uuids" do
@@ -137,7 +144,7 @@ describe CouchRest do
137
144
 
138
145
  describe "ensuring the db exists" do
139
146
  it "should be super easy" do
140
- db = CouchRest.database! "http://127.0.0.1:5984/couchrest-test-2"
147
+ db = CouchRest.database! "#{COUCHHOST}/couchrest-test-2"
141
148
  db.name.should == 'couchrest-test-2'
142
149
  db.info["db_name"].should == 'couchrest-test-2'
143
150
  end
@@ -180,7 +180,7 @@ describe CouchRest::Database do
180
180
 
181
181
  docs = [{'key' => 'value'}, {'_id' => 'totally-uniq'}]
182
182
  id_docs = [{'key' => 'value', '_id' => 'asdf6sgadkfhgsdfusdf'}, {'_id' => 'totally-uniq'}]
183
- CouchRest.should_receive(:post).with("http://127.0.0.1:5984/couchrest-test/_bulk_docs", {:docs => id_docs})
183
+ CouchRest.should_receive(:post).with("#{COUCHHOST}/couchrest-test/_bulk_docs", {:docs => id_docs})
184
184
 
185
185
  @db.bulk_save(docs)
186
186
  end
@@ -761,14 +761,14 @@ describe CouchRest::Database do
761
761
 
762
762
  shared_examples_for "continuously replicated" do
763
763
  it "contains the document from the original database" do
764
- sleep(1) # Allow some time to replicate
764
+ sleep(1.5) # Allow some time to replicate
765
765
  doc = @other_db.get('test_doc')
766
766
  doc['some-value'].should == 'foo'
767
767
  end
768
768
 
769
769
  it "contains documents saved after replication initiated" do
770
770
  @db.save_doc({'_id' => 'test_doc_after', 'some-value' => 'bar'})
771
- sleep(1) # Allow some time to replicate
771
+ sleep(1.5) # Allow some time to replicate
772
772
  doc = @other_db.get('test_doc_after')
773
773
  doc['some-value'].should == 'bar'
774
774
  end
@@ -134,5 +134,25 @@ describe CouchRest::Design do
134
134
  res["rows"].first["key"].should == ["a",2]
135
135
  end
136
136
  end
137
-
137
+
138
+ describe "a view with nil and 0 values" do
139
+ before(:all) do
140
+ @db = reset_test_db!
141
+ @des = CouchRest::Design.new
142
+ @des.name = "test"
143
+ @des.view_by :code
144
+ @des.database = @db
145
+ @des.save
146
+ @db.bulk_save([{"code" => "a", "age" => 2},
147
+ {"code" => nil, "age" => 4},{"code" => 0, "age" => 9}])
148
+ end
149
+ it "should work" do
150
+ res = @des.view :by_code
151
+ res["rows"][0]["key"].should == 0
152
+ res["rows"][1]["key"].should == "a"
153
+ res["rows"][2].should be_nil
154
+ end
155
+ end
156
+
157
+
138
158
  end
@@ -62,6 +62,10 @@ describe CouchRest::Document do
62
62
  @doc.rev.should be_nil
63
63
  @doc.id.should be_nil
64
64
  end
65
+ it "should be possible to set id" do
66
+ @doc.id = 1
67
+ @doc.id.should eql(1)
68
+ end
65
69
 
66
70
  it "should freak out when saving without a database" do
67
71
  lambda{@doc.save}.should raise_error(ArgumentError)
@@ -4,7 +4,7 @@ describe CouchRest::Server do
4
4
 
5
5
  describe "available databases" do
6
6
  before(:each) do
7
- @couch = CouchRest::Server.new
7
+ @couch = CouchRest::Server.new COUCHHOST
8
8
  end
9
9
 
10
10
  after(:each) do
@@ -8,10 +8,10 @@ unless defined?(FIXTURE_PATH)
8
8
  FIXTURE_PATH = File.join(File.dirname(__FILE__), '/fixtures')
9
9
  SCRATCH_PATH = File.join(File.dirname(__FILE__), '/tmp')
10
10
 
11
- COUCHHOST = "http://127.0.0.1:5984"
11
+ COUCHHOST = ENV['COUCHHOST'] || "http://127.0.0.1:5984"
12
12
  TESTDB = 'couchrest-test'
13
13
  REPLICATIONDB = 'couchrest-test-replication'
14
- TEST_SERVER = CouchRest.new
14
+ TEST_SERVER = CouchRest.new COUCHHOST
15
15
  TEST_SERVER.default_database = TESTDB
16
16
  DB = TEST_SERVER.database(TESTDB)
17
17
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couchrest
3
3
  version: !ruby/object:Gem::Version
4
- hash: -1848230054
4
+ hash: -1848230053
5
5
  prerelease: true
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
9
  - 0
10
- - beta2
11
- version: 1.0.0.beta2
10
+ - beta3
11
+ version: 1.0.0.beta3
12
12
  platform: ruby
13
13
  authors:
14
14
  - J. Chris Anderson
@@ -19,7 +19,7 @@ autorequire:
19
19
  bindir: bin
20
20
  cert_chain: []
21
21
 
22
- date: 2010-07-01 00:00:00 -03:00
22
+ date: 2010-07-25 00:00:00 -03:00
23
23
  default_executable:
24
24
  dependencies:
25
25
  - !ruby/object:Gem::Dependency
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirement: &id002 !ruby/object:Gem::Requirement
45
45
  none: false
46
46
  requirements:
47
- - - <=
47
+ - - "="
48
48
  - !ruby/object:Gem::Version
49
- hash: 3
49
+ hash: 23
50
50
  segments:
51
51
  - 1
52
- - 4
53
52
  - 2
54
- version: 1.4.2
53
+ - 4
54
+ version: 1.2.4
55
55
  type: :runtime
56
56
  version_requirements: *id002
57
57
  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.
@@ -96,7 +96,7 @@ files:
96
96
  - lib/couchrest/response.rb
97
97
  - lib/couchrest/rest_api.rb
98
98
  - lib/couchrest/server.rb
99
- - lib/couchrest/support/class.rb
99
+ - lib/couchrest/support/inheritable_attributes.rb
100
100
  - spec/couchrest/couchrest_spec.rb
101
101
  - spec/couchrest/database_spec.rb
102
102
  - spec/couchrest/design_spec.rb
@@ -1,190 +0,0 @@
1
- # Copyright (c) 2006-2009 David Heinemeier Hansson
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining
4
- # a copy of this software and associated documentation files (the
5
- # "Software"), to deal in the Software without restriction, including
6
- # without limitation the rights to use, copy, modify, merge, publish,
7
- # distribute, sublicense, and/or sell copies of the Software, and to
8
- # permit persons to whom the Software is furnished to do so, subject to
9
- # the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be
12
- # included in all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
- #
22
- # Extracted From
23
- # http://github.com/rails/rails/commit/971e2438d98326c994ec6d3ef8e37b7e868ed6e2
24
-
25
- # Extends the class object with class and instance accessors for class attributes,
26
- # just like the native attr* accessors for instance attributes.
27
- #
28
- # class Person
29
- # cattr_accessor :hair_colors
30
- # end
31
- #
32
- # Person.hair_colors = [:brown, :black, :blonde, :red]
33
- class Class
34
- def cattr_reader(*syms)
35
- syms.flatten.each do |sym|
36
- next if sym.is_a?(Hash)
37
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
38
- unless defined? @@#{sym} # unless defined? @@hair_colors
39
- @@#{sym} = nil # @@hair_colors = nil
40
- end # end
41
- #
42
- def self.#{sym} # def self.hair_colors
43
- @@#{sym} # @@hair_colors
44
- end # end
45
- #
46
- def #{sym} # def hair_colors
47
- @@#{sym} # @@hair_colors
48
- end # end
49
- EOS
50
- end
51
- end unless Class.respond_to?(:cattr_reader)
52
-
53
- def cattr_writer(*syms)
54
- options = syms.extract_options!
55
- syms.flatten.each do |sym|
56
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
57
- unless defined? @@#{sym} # unless defined? @@hair_colors
58
- @@#{sym} = nil # @@hair_colors = nil
59
- end # end
60
- #
61
- def self.#{sym}=(obj) # def self.hair_colors=(obj)
62
- @@#{sym} = obj # @@hair_colors = obj
63
- end # end
64
- #
65
- #{" #
66
- def #{sym}=(obj) # def hair_colors=(obj)
67
- @@#{sym} = obj # @@hair_colors = obj
68
- end # end
69
- " unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false
70
- EOS
71
- end
72
- end unless Class.respond_to?(:cattr_writer)
73
-
74
- def cattr_accessor(*syms)
75
- cattr_reader(*syms)
76
- cattr_writer(*syms)
77
- end unless Class.respond_to?(:cattr_accessor)
78
-
79
- # Defines class-level inheritable attribute reader. Attributes are available to subclasses,
80
- # each subclass has a copy of parent's attribute.
81
- #
82
- # @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
83
- # @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
84
- #
85
- # @api public
86
- #
87
- # @todo Do we want to block instance_reader via :instance_reader => false
88
- # @todo It would be preferable that we do something with a Hash passed in
89
- # (error out or do the same as other methods above) instead of silently
90
- # moving on). In particular, this makes the return value of this function
91
- # less useful.
92
- def extlib_inheritable_reader(*ivars)
93
- instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
94
-
95
- ivars.each do |ivar|
96
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
97
- def self.#{ivar}
98
- return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
99
- ivar = superclass.#{ivar}
100
- return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
101
- @#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) ? ivar.dup : ivar
102
- end
103
- RUBY
104
- unless instance_reader == false
105
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
106
- def #{ivar}
107
- self.class.#{ivar}
108
- end
109
- RUBY
110
- end
111
- end
112
- end unless Class.respond_to?(:extlib_inheritable_reader)
113
-
114
- # Defines class-level inheritable attribute writer. Attributes are available to subclasses,
115
- # each subclass has a copy of parent's attribute.
116
- #
117
- # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
118
- # define inheritable writer for.
119
- # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
120
- # @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
121
- #
122
- # @api public
123
- #
124
- # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
125
- # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
126
- def extlib_inheritable_writer(*ivars)
127
- instance_writer = ivars.pop[:writer] if ivars.last.is_a?(Hash)
128
- ivars.each do |ivar|
129
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
130
- def self.#{ivar}=(obj)
131
- @#{ivar} = obj
132
- end
133
- RUBY
134
- unless instance_writer == false
135
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
136
- def #{ivar}=(obj) self.class.#{ivar} = obj end
137
- RUBY
138
- end
139
-
140
- self.send("#{ivar}=", yield) if block_given?
141
- end
142
- end unless Class.respond_to?(:extlib_inheritable_writer)
143
-
144
- # Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
145
- # each subclass has a copy of parent's attribute.
146
- #
147
- # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
148
- # define inheritable accessor for.
149
- # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
150
- # @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
151
- #
152
- # @api public
153
- def extlib_inheritable_accessor(*syms, &block)
154
- extlib_inheritable_reader(*syms)
155
- extlib_inheritable_writer(*syms, &block)
156
- end unless Class.respond_to?(:extlib_inheritable_accessor)
157
- end
158
-
159
- class Array
160
- # Extracts options from a set of arguments. Removes and returns the last
161
- # element in the array if it's a hash, otherwise returns a blank hash.
162
- #
163
- # def options(*args)
164
- # args.extract_options!
165
- # end
166
- #
167
- # options(1, 2) # => {}
168
- # options(1, 2, :a => :b) # => {:a=>:b}
169
- def extract_options!
170
- last.is_a?(::Hash) ? pop : {}
171
- end unless Array.new.respond_to?(:extract_options!)
172
-
173
- # Wraps the object in an Array unless it's an Array. Converts the
174
- # object to an Array using #to_ary if it implements that.
175
- def self.wrap(object)
176
- case object
177
- when nil
178
- []
179
- when self
180
- object
181
- else
182
- if object.respond_to?(:to_ary)
183
- object.to_ary
184
- else
185
- [object]
186
- end
187
- end
188
- end unless Array.respond_to?(:wrap)
189
- end
190
-