slipcover 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ad54f411cbdcf51d91104e6a64a0ab1e3b5e8c47
4
+ data.tar.gz: 5a438e303c5409b40a23ff201fa9559b50d73bc9
5
+ SHA512:
6
+ metadata.gz: 0115fb08c97b683454d17a889c2506aabc017627007e60b57bb937c2aae9ebc4b6d216be24e28a6cb7cdc383b180bb1b8f23fb73a5e559f6eb51210d5b862dcb
7
+ data.tar.gz: 5327028a2b60a08526415b984d037ad0d04abfbf3ba6cc6cceae41df754415b4f460f40ae8977ee872ee3047ac34f4c49dc3883e2aaf950c7ba7470faa665c07
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-2.0.0@slipcover --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in slipcover.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 socialchorus
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Slipcover
2
+
3
+ Slipcover is a really lite wrapper around CouchDB. It comes with a Railtie to get your Rails app setup fast.
4
+ It is YAML configured allowing easy connection to servers, without imposing database constraints ... since
5
+ one of the best parts of CouchDB is the ease at which you can create new databases.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'slipcover'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install slipcover
20
+
21
+ ## Usage
22
+
23
+ ### With Rails
24
+ Put a `slipcover.yml` in your config directory. Here is a sample:
25
+
26
+ development:
27
+ host: 127.0.0.1
28
+ port: 5984
29
+
30
+ production:
31
+ host: socialcoders.couchservice.com
32
+ username: username
33
+ password: password
34
+
35
+ #### Create a database:
36
+
37
+ Slipcover::Database.new('my_database_name').create
38
+
39
+ This will create a database with the Rails environment appended to the back of it: `database_name_development`.
40
+ This is a great convenience for testing etc, since you can create many database on the fly and they won't conflict
41
+ with development or other databases of the same name.
42
+
43
+ #### Create some documents:
44
+
45
+ doc = Slipcover::Document.new('my_database_name', {foo: ['a', 'row', 'of', 'bars']})
46
+ doc.save
47
+
48
+ #### Write views is js to query them:
49
+
50
+ Create a directory inside the Rails app called: `slipcover_views`. Organize each view/index in its own directory with a `map.js`
51
+ file. Where appropriate also add a `reduce.js` function. Since these files are real js files, they can be tested via Jasmine,
52
+ or your favorite testing framework.
53
+
54
+ #### Put a design document in your database to capture these views/indexes
55
+
56
+ design_doc = Slipcover::DesignDocument.new('my_database_name', 'design_it_good')
57
+ design_doc.save
58
+
59
+ It constructs views from the default app/slipcover_views directory. If you want to customize where the views live, do some
60
+ dependency injection:
61
+
62
+ design_doc = Slipcover::DesignDocument.new('my_database_name', 'degign_it_good', my_custom_directory)
63
+ design_doc.save
64
+
65
+ #### Query Data
66
+
67
+ desgin_document = Slipcover::DesignDocument.new('my_database_name', 'degign_it_good')
68
+ query = Slipcover::Query.new(design_document, view_name)
69
+ query.all
70
+ query.all(key: 'foo')
71
+
72
+ ### Usages Sans-Rails
73
+
74
+ Put a YAML configuration file where it seems appropriate. Configure slipcover so that it knows where your YAML is:
75
+
76
+ Slipcover::Config.yaml_path = my_special_place
77
+
78
+ Let Slipcover know what environment your are operating under:
79
+
80
+ Slipcover.env = 'staging'
81
+
82
+ When constructing databases, Slipcover will use your yaml and environmental configuration to setup the right server.
83
+
84
+ If you are looking for more freedom, you can created databases with an optional second argument that is a server.
85
+
86
+ The slipcover views can also be put wherever you want and configured in at the Slipcover::Config level or the individual
87
+ design document.
88
+
89
+
90
+ ## Contributing
91
+
92
+ 1. Fork it
93
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
94
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
95
+ 4. Push to the branch (`git push origin my-new-feature`)
96
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/slipcover.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'json'
2
+ require 'rest-client'
3
+ require 'active_support/core_ext/hash'
4
+ require 'active_support/core_ext/module/delegation'
5
+
6
+ require "slipcover/version"
7
+ require "slipcover/http_adapter"
8
+ require "slipcover/server"
9
+ require "slipcover/config"
10
+ require "slipcover/database"
11
+ require "slipcover/document"
12
+ require "slipcover/design_document"
13
+ require "slipcover/query"
14
+
15
+ require 'slipcover/railtie' if defined?(Rails)
@@ -0,0 +1,22 @@
1
+ module Slipcover
2
+ class Config
3
+ class << self
4
+ attr_accessor :view_dir, :yaml_path
5
+ attr_writer :env, :server
6
+ end
7
+
8
+ def self.env
9
+ @env ||= ENV['RACK_ENV'] || 'development'
10
+ end
11
+
12
+ def self.server
13
+ @server ||= Server.new(yaml_path, env)
14
+ end
15
+
16
+ def self.rails!
17
+ self.env = Rails.env
18
+ self.yaml_path = Rails.root.join('config/slipcover.yml')
19
+ self.view_dir = Rails.root.join("/app/slipcover_views")
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ module Slipcover
2
+ class Database
3
+ attr_reader :name, :server
4
+
5
+ def initialize(name, server=nil)
6
+ @name = name
7
+ @server = server || Slipcover::Config.server
8
+ end
9
+
10
+ def url
11
+ "#{server.url}/#{name}_#{Slipcover::Config.env}"
12
+ end
13
+
14
+ def http_adapter
15
+ @http_adapter ||= HttpAdapter.new
16
+ end
17
+
18
+ delegate :get, :post, :put,
19
+ to: :http_adapter
20
+
21
+ def create
22
+ put(url)
23
+ rescue HttpAdapter::Conflict
24
+ # handled by the ensure, move along
25
+ ensure
26
+ return info
27
+ end
28
+
29
+ def delete
30
+ http_adapter.delete(url)
31
+ true
32
+ rescue Exception => e
33
+ false
34
+ end
35
+
36
+ def info
37
+ get(url)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,57 @@
1
+ module Slipcover
2
+ class DesignDocument
3
+ attr_reader :database_name, :name, :view_dir
4
+
5
+ def initialize(database_name, name, view_dir=nil)
6
+ @database_name = database_name
7
+ @name = name
8
+ @view_dir = view_dir || Slipcover::Config.view_dir
9
+ end
10
+
11
+ def document
12
+ @document ||= Document.new(database_name, {
13
+ id: document_id,
14
+ language: 'javascript',
15
+ views: views
16
+ })
17
+ end
18
+
19
+ delegate :url, :attributes, :id, :rev, :delete, :database,
20
+ to: :document
21
+
22
+ def fetch
23
+ document.fetch
24
+ document.attributes[:views].symbolize_keys!
25
+ document.attributes[:views].each {|key, hash| document.attributes[:views][key] = hash.symbolize_keys!}
26
+ document
27
+ end
28
+
29
+ def save
30
+ document.save
31
+ rescue HttpAdapter::ConflictError
32
+ document.fetch
33
+ document[:views] = views
34
+ document.save
35
+ end
36
+
37
+ def document_id
38
+ "_design/#{name}"
39
+ end
40
+
41
+ def [](key)
42
+ document[:views][key]
43
+ end
44
+
45
+ def views
46
+ Dir.entries(view_dir).inject({}) do |hash, path|
47
+ if !path.match(/\./)
48
+ dir = view_dir + "/" + path
49
+ hash[path.to_sym] ||= {}
50
+ hash[path.to_sym][:map] = File.read(dir + "/map.js")
51
+ hash[path.to_sym][:reduce] = File.read(dir + "/reduce.js") if File.exist?(dir + "/reduce.js")
52
+ end
53
+ hash
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,77 @@
1
+ module Slipcover
2
+ class Document
3
+ attr_accessor :attributes, :id, :rev
4
+ attr_reader :database_name
5
+
6
+ def initialize(database_name, attributes={})
7
+ @database_name = database_name
8
+ self.attributes = attributes.symbolize_keys
9
+ set_intrinsic_values
10
+ end
11
+
12
+ delegate :[], :[]=, to: :attributes
13
+
14
+ def http_adapter
15
+ @http_adapter ||= HttpAdapter.new
16
+ end
17
+
18
+ delegate :get, :post, :put,
19
+ to: :http_adapter
20
+
21
+ def save
22
+ http_method = id ? :put : :post
23
+ doc_url = id ? url : database.url
24
+
25
+ response = send(http_method, doc_url, attributes_for_save)
26
+ set_intrinsic_values(response)
27
+ end
28
+
29
+ def fetch
30
+ self.attributes = get(url)
31
+ set_intrinsic_values
32
+ end
33
+
34
+ def delete
35
+ http_adapter.delete("#{url}?rev=#{rev}")
36
+ set_intrinsic_values({})
37
+ nullify_intrinsic_attributes
38
+ true
39
+ rescue Exception => e
40
+ false
41
+ end
42
+
43
+ def url
44
+ raise ArgumentError.new('no document id') unless id
45
+ "#{database.url}/#{id}"
46
+ end
47
+
48
+ def database
49
+ return @database if @database
50
+ @database = Slipcover::Database.new(database_name)
51
+ @database.create
52
+ @database
53
+ end
54
+
55
+ private
56
+
57
+ def attributes_for_save
58
+ attrs = attributes.clone
59
+ attrs[:_rev] = rev if rev
60
+ attrs[:_id] = id if id
61
+ attrs
62
+ end
63
+
64
+ def set_intrinsic_values(attrs=nil)
65
+ attrs ||= attributes
66
+ self.id = attrs[:id] || attrs[:_id]
67
+ self.rev = attrs[:rev] || attrs[:_rev]
68
+ end
69
+
70
+ def nullify_intrinsic_attributes
71
+ attributes[:_id] = nil
72
+ attributes[:id] = nil
73
+ attributes[:_rev] = nil
74
+ attributes[:rev] = nil
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,74 @@
1
+ module Slipcover
2
+ class HttpAdapter
3
+ def get(url, data={})
4
+ try {
5
+ parse( RestClient.get(url + query_string(data), headers) )
6
+ }
7
+ end
8
+
9
+ def post(url, data={})
10
+ try {
11
+ parse( RestClient.post(url, data.to_json, headers) )
12
+ }
13
+ end
14
+
15
+ def put(url, data={})
16
+ try {
17
+ parse( RestClient.put(url, data.to_json, headers) )
18
+ }
19
+ end
20
+
21
+ def delete(url, data={})
22
+ try {
23
+ parse( RestClient.delete(url + query_string(data), headers) )
24
+ }
25
+ end
26
+
27
+ def query_string(data)
28
+ return "" if data.empty?
29
+
30
+ query = data.map do |key, value|
31
+ "#{key}=#{value}"
32
+ end.join("&")
33
+
34
+ "?#{query}"
35
+ end
36
+
37
+ def parse(response)
38
+ JSON.parse(response).symbolize_keys
39
+ end
40
+
41
+ def try
42
+ yield
43
+ rescue Exception => e
44
+ reraise(e)
45
+ end
46
+
47
+ def reraise(e)
48
+ raise error_class(e).new(e.message)
49
+ end
50
+
51
+ def error_class(e)
52
+ {
53
+ JSON::ParserError => ParseError,
54
+ RestClient::ResourceNotFound => NotFound,
55
+ RestClient::PreconditionFailed => ConflictError,
56
+ RestClient::Conflict => ConflictError
57
+
58
+ }[e.class] || e.class
59
+ end
60
+
61
+ class ConflictError < RuntimeError
62
+ end
63
+
64
+ class ParseError < RuntimeError
65
+ end
66
+
67
+ class NotFound < RuntimeError
68
+ end
69
+
70
+ def headers
71
+ {:content_type => :json, :accept => :json}
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,45 @@
1
+ module Slipcover
2
+ class Query
3
+ attr_reader :design_document, :view_name
4
+
5
+ def initialize(design_document, view_name)
6
+ @design_document = design_document
7
+ @view_name = view_name
8
+ end
9
+
10
+ def http_adapter
11
+ @http_adapter ||= HttpAdapter.new
12
+ end
13
+
14
+ delegate :get,
15
+ to: :http_adapter
16
+
17
+ def url
18
+ "#{design_document.url}/_view/#{view_name}" # todo adapter takes opts and converts to query string
19
+ end
20
+
21
+ def all(opts={})
22
+ get(url, repackage(opts))[:rows].map{|row| Document.new(database.name, row['doc'].symbolize_keys) }
23
+ end
24
+
25
+ def repackage(opts)
26
+ opts.each do |key, value|
27
+ opts[key] = escape(value) if escape_key?(key)
28
+ end
29
+
30
+ {include_docs: true}.merge(opts)
31
+ end
32
+
33
+ def database
34
+ design_document.database
35
+ end
36
+
37
+ def escape(value)
38
+ URI.escape(value.inspect)
39
+ end
40
+
41
+ def escape_key?(key)
42
+ [:key, :keys, :startkey, :startkey_docid, :endkey, :endkey_docid, :stale].include?(key.to_sym)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,7 @@
1
+ module Slipcover
2
+ class Railtie < Rails::Railtie
3
+ initializer "slipcover.configure_rails_initialization" do
4
+ Slipcover::Config.rails!
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ module Slipcover
2
+ class Server < Struct.new(:path, :key)
3
+ def server_configs
4
+ YAML.load(File.read(path))
5
+ end
6
+
7
+ def config
8
+ @config ||= server_configs[key]
9
+ end
10
+
11
+ def url
12
+ "#{user_info}#{host}#{port}"
13
+ end
14
+
15
+ def user_info
16
+ info = config['username'] || ""
17
+ info << ":#{config['password']}" if config['password']
18
+ info << "@" unless info.empty?
19
+ info
20
+ end
21
+
22
+ def host
23
+ config['host']
24
+ end
25
+
26
+ def port
27
+ config['port'] ? ":#{config['port']}" : ''
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module Slipcover
2
+ VERSION = "0.1.0"
3
+ end
data/slipcover.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'slipcover/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "slipcover"
8
+ spec.version = Slipcover::VERSION
9
+ spec.authors = ["Kane Baccigalupi", "Deepti Anand", "Fito von Zastow", 'SocialCoders @ SocialChorus']
10
+ spec.email = ["baccigalupi@gmail.com", "developers@socialchorus.com"]
11
+ spec.description = %q{Lite wrapper for CouchDB}
12
+ spec.summary = %q{Lite wrapper for CouchDB}
13
+ spec.homepage = "http://github.com/socialchorus/slipcover"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "rest-client"
22
+ spec.add_dependency 'activesupport'
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency 'rspec'
27
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe Slipcover::Database, 'functional to the Slipcover database' do
4
+ let(:database) { Slipcover::Database.new(name, server) }
5
+ let(:name) { 'database_name' }
6
+ let(:server) { Slipcover::Server.new(File.dirname(__FILE__) + "/support/slipcover.yml", 'development') }
7
+ let(:database_url) { "#{database.server.url}/database_name_development" }
8
+
9
+ def ensure_database
10
+ RestClient.delete(database_url) rescue nil
11
+ RestClient.put(database_url, {}.to_json, {:content_type => :json, :accept => :json})
12
+ end
13
+
14
+ after do
15
+ RestClient.delete(database_url) rescue nil
16
+ end
17
+
18
+ describe '#info' do
19
+ before do
20
+ ensure_database
21
+ end
22
+
23
+ it "return information about the database" do
24
+ info = database.info
25
+ info.should be_a(Hash)
26
+ info.keys.should include(:doc_count)
27
+ end
28
+ end
29
+
30
+ describe "#create" do
31
+ context 'database does not exist' do
32
+ before do
33
+ RestClient.delete(database_url) rescue nil
34
+ end
35
+
36
+ it 'creates the database' do
37
+ info = database.create
38
+ info.should be_a(Hash)
39
+ info.keys.should include(:doc_count)
40
+ end
41
+ end
42
+
43
+ context 'database already exists' do
44
+ before do
45
+ ensure_database
46
+ end
47
+
48
+ it 'returns info about the existing database' do
49
+ info = database.create
50
+ info.should be_a(Hash)
51
+ info.keys.should include(:doc_count)
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "#delete" do
57
+ context 'database exists' do
58
+ before do
59
+ ensure_database
60
+ end
61
+
62
+ it "deletes the database" do
63
+ database.delete.should == true
64
+
65
+ expect {
66
+ database.info
67
+ }.to raise_error( Slipcover::HttpAdapter::NotFound )
68
+ end
69
+ end
70
+
71
+ context 'database does not exist' do
72
+ before do
73
+ RestClient.delete(database_url) rescue nil
74
+ end
75
+
76
+ it "returns false" do
77
+ database.delete.should == false
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe Slipcover::DesignDocument do
4
+ let(:document) { Slipcover::DesignDocument.new(database_name, name, view_dir) }
5
+ let(:database_name) { 'design_me' }
6
+ let(:database) { Slipcover::Database.new(database_name) }
7
+ let(:name) { 'designation' }
8
+ let(:view_dir) { support_dir + "/slipcover_views" }
9
+
10
+ let(:support_dir) { File.dirname(__FILE__) + "/support" }
11
+ let(:map_function) { File.read(view_dir + "/by_name/map.js") }
12
+
13
+ before do
14
+ database.delete
15
+ database.create
16
+ end
17
+
18
+ it "defaults to javascript as a language" do
19
+ document.attributes[:language].should == 'javascript'
20
+ end
21
+
22
+ it "has the right url" do
23
+ document.url.should == "#{database.url}/_design/designation"
24
+ end
25
+
26
+ describe 'views' do
27
+ it "finds them via the mapping of the name to the view directory" do
28
+ document[:by_name][:map].should == map_function
29
+ end
30
+ end
31
+
32
+ describe '#save' do
33
+ context "when the design document does not yet exist" do
34
+ it "saves it and delegates good stuff to it" do
35
+ document.save
36
+ document.fetch
37
+ document[:by_name][:map].should == map_function
38
+ document.id.should_not be_nil
39
+ document.rev.should_not be_nil
40
+ end
41
+ end
42
+
43
+ context 'when the design document already exists' do
44
+ it "updates it without raising an error" do
45
+ document.save
46
+
47
+ duplicate_document = Slipcover::DesignDocument.new(database_name, name, view_dir)
48
+
49
+ duplicate_document.save
50
+ duplicate_document.rev.should_not be_nil
51
+
52
+ # expect { duplicate_document.save }.not_to raise_error
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe Slipcover::Document, 'functional' do
4
+ let(:document) { Slipcover::Document.new(database_name, attributes) }
5
+ let(:database) { Slipcover::Database.new(database_name) }
6
+ let(:database_name) { 'hello_database' }
7
+ let(:attributes) {
8
+ {
9
+ type: 'foo',
10
+ author: 'something'
11
+ }
12
+ }
13
+
14
+ before do
15
+ Slipcover::Config.server ||= Slipcover::Server.new(File.dirname(__FILE__) + "/support/slipcover.yml", 'development')
16
+ database.create
17
+ end
18
+
19
+ after do
20
+ database.delete
21
+ end
22
+
23
+ describe '#initialize' do
24
+ let(:attributes) {
25
+ {
26
+ id: '298c2fd111c14633e445ef818b039ff3-us',
27
+ rev: '2-298c2fd111c14633e445ef818b039ff3',
28
+ type: 'foo',
29
+ author: 'something'
30
+ }
31
+ }
32
+
33
+ it "has an id" do
34
+ document.id.should == attributes[:id]
35
+ end
36
+
37
+ it "has a rev" do
38
+ document.rev.should == attributes[:rev]
39
+ end
40
+ end
41
+
42
+ describe '#fetch' do
43
+ before do
44
+ document.save
45
+ document.rev = nil
46
+ document.attributes = {}
47
+ document.fetch
48
+ end
49
+
50
+ it "should return all the attributes" do
51
+ document.attributes[:type].should == 'foo'
52
+ document.attributes[:author].should == 'something'
53
+ end
54
+
55
+ it "should update the rev" do
56
+ document.fetch
57
+ document.rev.should_not be_nil
58
+ end
59
+
60
+ it 'should raise an error if id does not exist' do
61
+ document.id = nil
62
+ expect{ document.fetch }.to raise_error
63
+ end
64
+ end
65
+
66
+ describe '#save' do
67
+ context 'when there is no id' do
68
+ before do
69
+ document.save
70
+ end
71
+
72
+ it "gets the id from the server response" do
73
+ document.id.should_not be_nil
74
+ end
75
+
76
+ it "gets the rev from the server response" do
77
+ document.rev.should_not be_nil
78
+ end
79
+
80
+ it 'should maintains its local attributes' do
81
+ document.attributes[:type].should == 'foo'
82
+ document.attributes[:author].should == 'something'
83
+ end
84
+
85
+ it "is stored in the database, and available" do
86
+ expect {
87
+ RestClient.get(document.url)
88
+ }.not_to raise_error
89
+ document.fetch
90
+ document.attributes[:type].should == 'foo'
91
+ end
92
+ end
93
+
94
+ context 'when there is an id passed in' do
95
+ before do
96
+ document.save
97
+ document.attributes[:bar] = 'what?'
98
+ end
99
+
100
+ it 'gets a new revision' do
101
+ original_rev = document.rev.clone
102
+ document.save
103
+ document.rev.should_not == original_rev
104
+ end
105
+
106
+ it 'updates the database' do
107
+ document.save
108
+ document.fetch
109
+ document.attributes[:bar].should == 'what?'
110
+ end
111
+
112
+ it "does not create another document" do
113
+ document.database.info[:doc_count].should == 1
114
+ end
115
+ end
116
+ end
117
+
118
+ describe '#delete' do
119
+ context 'when the document is on the server' do
120
+ before do
121
+ document.save
122
+ document[:_rev] = '_rev'
123
+ document[:rev] = 'rev'
124
+ document[:_id] = '_id'
125
+ document[:id] = 'id'
126
+ end
127
+
128
+ it 'should delete the record from the database' do
129
+ url = document.url
130
+ document.delete
131
+ expect {
132
+ RestClient.get(url)
133
+ }.to raise_error
134
+ end
135
+
136
+ it "should return true" do
137
+ document.delete.should == true
138
+ end
139
+
140
+ it 'should clear the id and rev' do
141
+ document.delete
142
+ document.id.should be_nil
143
+ document.rev.should be_nil
144
+ document[:_id].should be_nil
145
+ document[:id].should be_nil
146
+ document[:_rev].should be_nil
147
+ document[:rev].should be_nil
148
+ end
149
+ end
150
+
151
+ context 'when the document is not on the sever' do
152
+ it "should return false" do
153
+ document.delete.should == false
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Slipcover::HttpAdapter do
4
+ let(:adapter) { Slipcover::HttpAdapter.new }
5
+
6
+
7
+ describe '#get' do
8
+ let(:response) { {}.to_json }
9
+
10
+ before do
11
+ RestClient.stub(:get).and_return(response)
12
+ end
13
+
14
+ it "should package up data into a query string" do
15
+ RestClient.should_receive(:get).with('http://url.com?foo=bar', anything).and_return(response)
16
+ adapter.get('http://url.com', {foo: 'bar'})
17
+ end
18
+ end
19
+
20
+ describe '#delete' do
21
+ let(:response) { {}.to_json }
22
+
23
+ before do
24
+ RestClient.stub(:delete).and_return(response)
25
+ end
26
+
27
+ it "should package up data into a query string" do
28
+ RestClient.should_receive(:delete).with('http://url.com?foo=bar', anything).and_return(response)
29
+ adapter.delete('http://url.com', {foo: 'bar'})
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Slipcover::Query do
4
+ let(:query) { Slipcover::Query.new(design_document, :by_name) }
5
+ let(:design_document) { Slipcover::DesignDocument.new(database_name, name, view_dir) }
6
+
7
+ let(:database_name) { 'my_database_name' }
8
+ let(:name) { 'designation' }
9
+ let(:view_dir) { support_dir + "/slipcover_views" }
10
+ let(:support_dir) { File.dirname(__FILE__) + "/support" }
11
+
12
+ before do
13
+ db = Slipcover::Database.new(database_name)
14
+ db.delete
15
+ db.create
16
+
17
+ design_document.save
18
+
19
+ Slipcover::Document.new(database_name, {name: 'Deepti'}).save
20
+ Slipcover::Document.new(database_name, {name: 'Fito'}).save
21
+ Slipcover::Document.new(database_name, {name: 'Kane'}).save
22
+ Slipcover::Document.new(database_name, {animal: 'gerbil'}).save
23
+ end
24
+
25
+ after do
26
+ db = Slipcover::Database.new(database_name)
27
+ db.delete
28
+ end
29
+
30
+ describe '#all' do
31
+ it 'returns all the documents that match the index/view' do
32
+ results = query.all
33
+ results.size.should == 3
34
+ results.map {|doc| doc[:name] }.should =~ ['Deepti', 'Fito', 'Kane']
35
+ end
36
+
37
+ context 'with options' do
38
+ context 'with key' do
39
+ it "returns only the related records" do
40
+ results = query.all({key: 'Deepti'})
41
+ results.size.should == 1
42
+ results.first[:name].should == 'Deepti'
43
+ end
44
+ end
45
+
46
+ context 'with a startkey and endkey' do
47
+ it "return the range of docs" do
48
+ results = query.all({startkey: 'Da', endkey: 'Fz'})
49
+ results.size.should == 2
50
+ results.map{ |doc| doc[:name] }.should =~ ['Deepti', 'Fito']
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Slipcover::Server do
4
+ let(:server) { Slipcover::Server.new(config_path, env) }
5
+ let(:config_path) { File.dirname(__FILE__) + "/support/slipcover.yml" }
6
+ let(:env) { 'development' }
7
+
8
+ context "when the host is a local address" do
9
+ describe "#url" do
10
+ it 'returns the right url' do
11
+ server.url.should == "127.0.0.1:5984"
12
+ end
13
+ end
14
+ end
15
+
16
+ context "when the host is a remote address" do
17
+ let(:env) { 'production' }
18
+ describe "#url" do
19
+ it 'returns the right url' do
20
+ server.url.should == "username:password@socialcoders.couchserver.com"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ $: << File.dirname(__FILE__) + "/../lib"
8
+ require 'slipcover'
9
+
10
+ Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each { |f| require f }
11
+
12
+ RSpec.configure do |config|
13
+ config.treat_symbols_as_metadata_keys_with_true_values = true
14
+ config.run_all_when_everything_filtered = true
15
+ config.filter_run :focus
16
+
17
+ # Run specs in random order to surface order dependencies. If you find an
18
+ # order dependency and want to debug it, you can fix the order by providing
19
+ # the seed, which is printed after each run.
20
+ # --seed 1234
21
+ config.order = 'random'
22
+
23
+ config.before do
24
+ Slipcover::Config.yaml_path = File.dirname(__FILE__) + "/support/slipcover.yml"
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ development:
2
+ host: 127.0.0.1
3
+ port: 5984
4
+
5
+ production:
6
+ host: socialcoders.couchserver.com
7
+ username: username
8
+ password: password
@@ -0,0 +1,3 @@
1
+ function (doc) {
2
+ if (doc.name) emit(doc.name);
3
+ }
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: slipcover
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kane Baccigalupi
8
+ - Deepti Anand
9
+ - Fito von Zastow
10
+ - SocialCoders @ SocialChorus
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2014-05-14 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rest-client
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: activesupport
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: bundler
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ~>
49
+ - !ruby/object:Gem::Version
50
+ version: '1.3'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: '1.3'
58
+ - !ruby/object:Gem::Dependency
59
+ name: rake
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ - !ruby/object:Gem::Dependency
73
+ name: rspec
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ description: Lite wrapper for CouchDB
87
+ email:
88
+ - baccigalupi@gmail.com
89
+ - developers@socialchorus.com
90
+ executables: []
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - .gitignore
95
+ - .rspec
96
+ - .rvmrc
97
+ - Gemfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - lib/slipcover.rb
102
+ - lib/slipcover/config.rb
103
+ - lib/slipcover/database.rb
104
+ - lib/slipcover/design_document.rb
105
+ - lib/slipcover/document.rb
106
+ - lib/slipcover/http_adapter.rb
107
+ - lib/slipcover/query.rb
108
+ - lib/slipcover/railtie.rb
109
+ - lib/slipcover/server.rb
110
+ - lib/slipcover/version.rb
111
+ - slipcover.gemspec
112
+ - spec/database_spec.rb
113
+ - spec/design_document_spec.rb
114
+ - spec/document_spec.rb
115
+ - spec/http_adapter_spec.rb
116
+ - spec/query_spec.rb
117
+ - spec/server_spec.rb
118
+ - spec/spec_helper.rb
119
+ - spec/support/slipcover.yml
120
+ - spec/support/slipcover_views/by_name/map.js
121
+ homepage: http://github.com/socialchorus/slipcover
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.0.3
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Lite wrapper for CouchDB
145
+ test_files:
146
+ - spec/database_spec.rb
147
+ - spec/design_document_spec.rb
148
+ - spec/document_spec.rb
149
+ - spec/http_adapter_spec.rb
150
+ - spec/query_spec.rb
151
+ - spec/server_spec.rb
152
+ - spec/spec_helper.rb
153
+ - spec/support/slipcover.yml
154
+ - spec/support/slipcover_views/by_name/map.js