slipcover 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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