therealadam-couchrest-rails 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.gitignore +2 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +136 -0
  4. data/Rakefile +24 -0
  5. data/VERSION +1 -0
  6. data/generators/couch_rest_rails/USAGE +8 -0
  7. data/generators/couch_rest_rails/couch_rest_rails_generator.rb +11 -0
  8. data/generators/couch_rest_rails/templates/couchdb.yml +13 -0
  9. data/generators/couch_rest_rails/templates/couchdb_initializer.rb +36 -0
  10. data/init.rb +7 -0
  11. data/install.rb +3 -0
  12. data/lib/autotest/discover.rb +5 -0
  13. data/lib/couch_rest_rails.rb +12 -0
  14. data/lib/couch_rest_rails/database.rb +64 -0
  15. data/lib/couch_rest_rails/document.rb +12 -0
  16. data/lib/couch_rest_rails/fixtures.rb +54 -0
  17. data/lib/couch_rest_rails/lucene.rb +61 -0
  18. data/lib/couch_rest_rails/tests.rb +53 -0
  19. data/lib/couch_rest_rails/views.rb +72 -0
  20. data/lib/couch_rest_tasks.rb +54 -0
  21. data/lib/spec/rails/matchers/couch_document_validations.rb +44 -0
  22. data/spec/lib/couch_rest_rails/database_spec.rb +44 -0
  23. data/spec/lib/couch_rest_rails/document_spec.rb +22 -0
  24. data/spec/lib/couch_rest_rails/fixtures_spec.rb +41 -0
  25. data/spec/lib/couch_rest_rails/tests_spec.rb +45 -0
  26. data/spec/lib/couch_rest_rails/views_spec.rb +25 -0
  27. data/spec/lib/couch_rest_rails_spec.rb +32 -0
  28. data/spec/matchers/couch_document_validations_spec.rb +45 -0
  29. data/spec/mock/fixtures/bars.yml +11 -0
  30. data/spec/mock/fixtures/foos.yml +11 -0
  31. data/spec/mock/views/bars/all/map.js +9 -0
  32. data/spec/mock/views/foos/all/map.js +9 -0
  33. data/spec/mock/views/foos/tags/map.js +7 -0
  34. data/spec/mock/views/foos/tags/reduce.js +3 -0
  35. data/spec/spec_helper.rb +6 -0
  36. data/tasks/couch_rest_rails_tasks.rake +1 -0
  37. data/uninstall.rb +1 -0
  38. metadata +96 -0
@@ -0,0 +1,2 @@
1
+ pkg
2
+
@@ -0,0 +1,20 @@
1
+ Copyright (c) Henry Poydar
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.
@@ -0,0 +1,136 @@
1
+ # CouchRest-Rails
2
+
3
+ A Rails plugin for connecting to and working with a [CouchDB](http://couchdb.apache.org) document-oriented database via the [CouchRest](http://github.com/jchris/couchrest) RESTful CouchDB client.
4
+
5
+ Specifically, this plugin provides the following utilities:
6
+
7
+ * Initializer for use with a couchdb.yml configuration file
8
+ * CouchDB-specific rake tasks (database creation, deletion, fixture loading, views synchronization)
9
+ * CouchDB-specific fixtures
10
+ * Setup and teardown helpers for spec'ing and testing
11
+ * A paper-thin wrapper around CouchRest::ExtendedDocument
12
+
13
+ This plugin currently assumes your application only uses one CouchDB database. It does not interfere with the traditional relational database backend, so you can use that as a datastore alongside CouchDB if you want. (In fact, you'll have to unwire the requirement for a relational database if you don't want to use one.)
14
+
15
+ ## Requirements
16
+
17
+ * [CouchRest gem](http://github.com/jchris/couchrest)
18
+ * [RSpec](http://github.com/dchelimsky/rspec) BDD framework (optional - for running plugin specs)
19
+ * [RSpec-Rails](http://github.com/dchelimsky/rspec-rails) library (optional - for running plugin specs)
20
+
21
+
22
+ ## Installation
23
+
24
+ Install with the native Rails plugin installation script:
25
+
26
+ script/plugin install git://github.com/hpoydar/couchrest_rails.git
27
+
28
+ Or simply add to vendor/plugins and generate the files you need:
29
+
30
+ script/generate couch_rest_rails relax
31
+
32
+ The plugin creates two folder:
33
+
34
+ * `db/couch/` - for storing CouchDB database information map and reduce functions (views) and lucene indexing (lucence
35
+ * `test/fixtures/couch` - for storing and loading CouchDB fixtures (yaml)
36
+
37
+ These paths can be customized in an initializer or environment configuration file:
38
+
39
+ CouchRestRails.fixtures_path = 'custom/path/to/your/fixtures/from/app/root'
40
+ CouchRestRails.views_path = 'custom/path/to/your/views/from/app/root'
41
+
42
+ ## Usage
43
+
44
+ ### Rake tasks
45
+
46
+ Use the rake tasks to create, delete, reset, push views and load fixtures:
47
+
48
+ rake -T | grep couchdb
49
+
50
+ ### Tests and specs
51
+
52
+ For testing or spec'ing, use these helpers to setup and teardown a test database with fixtures:
53
+
54
+ CouchRestRails::Tests.setup
55
+ CouchRestRails::Tests.teardown
56
+
57
+ There are also some simple matchers you can can use to spec validations. See `spec/lib/matchers`.
58
+
59
+ ### CouchRestRails document model
60
+
61
+ For models, inherit from CouchRestRails::Document, which hooks up CouchRest::ExtendedDocument to your CouchDB backend and includes the [Validatable](http://validatable.rubyforge.org/) module:
62
+
63
+ class YourCouchDocument < CouchRestRails::Document
64
+ use_database :database_name
65
+
66
+ property :email
67
+ property :question
68
+ property :answer
69
+ property :rating
70
+
71
+ timestamps!
72
+
73
+ view_by :email
74
+
75
+ validates_presence_of :question
76
+ validates_numericality_of :rating
77
+
78
+ ...
79
+
80
+ end
81
+
82
+ See the CouchRest documentation and specs for more information about CouchRest::ExtendedDocument. (The views defined here are in addition to the ones you can manually set up and push via rake in db/couch/views.)
83
+
84
+ ### CouchDB views
85
+
86
+ Custom views--outside of the ones defined in your CouchRestRails::Document models--that you want to push up to the CouchDB database/server instance should be in the following format:
87
+
88
+ db/couch/<database_name>/views
89
+ |-- <design_document_name>
90
+ |-- <view_name>
91
+ |-- map.js
92
+ `-- reduce.js
93
+ /lucene
94
+ |-- <design_document_name>
95
+ |-- <lucene_search>
96
+
97
+ Push up your views via rake (`rake couchdb:views:push`) or within your code or console (`CouchRestRails::Views.push`).
98
+
99
+ Push up your lucene doc via rake (`rake couchdb:lucence:push`) or within your code or console (`CouchRestRails::Lucene.push`).
100
+
101
+ ## Further development and testing
102
+
103
+ To run the test suite, you'll need rspec installed with rspec-rails library enabled for the host application. You can run the tests in the following way:
104
+
105
+ <rails_root>$ rake spec:plugins
106
+ <plugin_root>$ rake spec
107
+ <plugin_root>$ autospec
108
+
109
+ (The latter requires the ZenTest gem)
110
+
111
+ # Rails integration unit testing
112
+
113
+ Create fixture file by via rake (`rake couchdb:fixture:dump[<database_name>]`) or within your code or console (`CouchRestRails::Fixtures.dump[<database_name>]`).
114
+
115
+ Add fixtures to rails test:
116
+
117
+ class RailsTest < Test::Unit::TestCase
118
+ couchdb_fixtures :<database_name>
119
+
120
+ ...
121
+
122
+ end
123
+
124
+ ## TODO
125
+
126
+ * Roll up CouchRest::ExtendedDocument, since it might be deprecated from CouchRest (see CouchRest raw branch)
127
+ * A persistent connection object? Keep-alive?
128
+ * Hook into Rails logger to display times for CouchDB operations
129
+ * Mechanism for better view testing?
130
+ * Restful model/controller/test/spec generator
131
+ * Gemify
132
+ * Add more parseable options to couchdb.yml
133
+
134
+ ## License
135
+
136
+ Copyright (c) Henry Poydar, released under the MIT license
@@ -0,0 +1,24 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ desc 'Default: run specs.'
5
+ task :default => :spec
6
+
7
+ desc 'Run the specs'
8
+ Spec::Rake::SpecTask.new(:spec) do |t|
9
+ t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
10
+ t.spec_files = FileList['spec/**/*_spec.rb']
11
+ end
12
+
13
+ begin
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gemspec|
16
+ gemspec.name = 'couchrest-rails'
17
+ gemspec.summary = 'Make working with CouchDB and Rails even better.'
18
+ gemspec.description = 'Rails plugin for connecting to and working with CouchDB via CouchRest'
19
+ gemspec.homepage = 'http://github.com/therealadam/couchrest-rails'
20
+ gemspec.authors = ['Henry Poydar']
21
+ end
22
+ rescue LoadError
23
+ puts "Jeweler not available. Can't build RubyGems."
24
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Generates the files needed for your Rails application to connect to and work with a CouchDB database over CouchRest.
3
+
4
+ Example:
5
+ ./script/generate couchrest_rails relax
6
+
7
+ This will create:
8
+ config/initializers/couchdb.rb
@@ -0,0 +1,11 @@
1
+ class CouchRestRailsGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+
5
+ m.directory "db/couch"
6
+ m.directory "test/fixtures/couch"
7
+ m.template "couchdb.yml", "config/couchdb.yml"
8
+ m.template "couchdb_initializer.rb", "config/initializers/couchdb.rb"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ base: &base
2
+ database_prefix:
3
+ database_suffix: _<%=RAILS_ENV%>
4
+
5
+ development:
6
+ host: localhost
7
+ port: 5984
8
+ <<: *base
9
+
10
+ test:
11
+ host: localhost
12
+ port: 5984
13
+ <<: *base
@@ -0,0 +1,36 @@
1
+ begin
2
+
3
+ env = ENV['RAILS_ENV'] || 'development'
4
+
5
+ couchdb_config = YAML::load(ERB.new(IO.read(RAILS_ROOT + "/config/couchdb.yml")).result)[env]
6
+
7
+ host = couchdb_config["host"] || 'localhost'
8
+ port = couchdb_config["port"] || '5984'
9
+ database = couchdb_config["database"]
10
+ username = couchdb_config["username"]
11
+ password = couchdb_config["password"]
12
+ ssl = couchdb_config["ssl"] || false
13
+ db_prefix = couchdb_config["database_prefix"] || ""
14
+ db_suffix = couchdb_config["database_suffix"] || ""
15
+ host = "localhost" if host == nil
16
+ port = "5984" if port == nil
17
+ ssl = false if ssl == nil
18
+
19
+ protocol = ssl ? 'https' : 'http'
20
+ authorized_host = (username.blank? && password.blank?) ? host :
21
+ "#{CGI.escape(username)}:#{CGI.escape(password)}@#{host}"
22
+
23
+ rescue
24
+
25
+ raise "There was a problem with your config/couchdb.yml file. Check and make sure it's present and the syntax is correct."
26
+
27
+ else
28
+
29
+ COUCHDB_CONFIG = {
30
+ :host_path => "#{protocol}://#{authorized_host}:#{port}",
31
+ :db_prefix => "#{db_prefix}",
32
+ :db_suffix => "#{db_suffix}"
33
+ }
34
+
35
+ COUCHDB_SERVER = CouchRest.new COUCHDB_CONFIG[:host_path]
36
+ end
data/init.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'couch_rest_rails'
2
+ require 'validatable'
3
+ require 'spec/rails/matchers/couch_document_validations'
4
+
5
+ config.gem 'couchrest'
6
+ config.gem 'validatable'
7
+ config.gem 'json'
@@ -0,0 +1,3 @@
1
+ require 'rails_generator'
2
+ require 'rails_generator/scripts/generate'
3
+ Rails::Generator::Scripts::Generate.new.run(['couchrest_rails', 'relax'], :destination => RAILS_ROOT)
@@ -0,0 +1,5 @@
1
+ $:.push(File.join(File.dirname(__FILE__), %w[.. .. rspec]))
2
+
3
+ Autotest.add_discovery do
4
+ "rspec"
5
+ end
@@ -0,0 +1,12 @@
1
+ module CouchRestRails
2
+
3
+ mattr_accessor :test_environment
4
+ self.test_environment = 'test'
5
+
6
+ mattr_accessor :setup_path
7
+ self.setup_path = 'db/couch'
8
+
9
+ mattr_accessor :fixture_path
10
+ self.fixture_path = 'test/fixtures/couch'
11
+
12
+ end
@@ -0,0 +1,64 @@
1
+ module CouchRestRails
2
+ module Database
3
+
4
+ extend self
5
+
6
+ def create(database)
7
+ resp = []
8
+ return "Database '#{database}' doesn't exists" unless
9
+ (database == "*" || File.exist?(File.join(RAILS_ROOT,
10
+ CouchRestRails.setup_path,
11
+ database)))
12
+
13
+ # get list of available_databases in couch...
14
+ existing_databases = COUCHDB_SERVER.databases
15
+ # get all the model files
16
+ Dir[File.join(RAILS_ROOT, CouchRestRails.setup_path,database)].each do |db|
17
+ # check for a directory...
18
+ if File::directory?( db )
19
+ database_name =COUCHDB_CONFIG[:db_prefix] + File.basename(db) +
20
+ COUCHDB_CONFIG[:db_suffix]
21
+ if existing_databases.include?(database_name)
22
+ resp << "The CouchDB database '#{database_name}' already exists"
23
+ else
24
+ # create the database
25
+ COUCHDB_SERVER.create_db(database_name)
26
+ resp << "Created the CouchDB database '#{database_name}'"
27
+ # create views on database
28
+ resp << CouchRestRails::Views.push(File.basename(db),"*")
29
+ # create lucene-searches
30
+ resp << CouchRestRails::Lucene.push(File.basename(db),"*")
31
+ end
32
+ end
33
+ end
34
+ resp << "create complete"
35
+ resp.join("\n")
36
+ end
37
+
38
+ def delete(database)
39
+ resp = []
40
+ return "Database '#{database}' doesn't exists" unless
41
+ (database == "*" || File.exist?(File.join(RAILS_ROOT,
42
+ CouchRestRails.setup_path,
43
+ database)))
44
+ # get list of available_databases in couch...
45
+ existing_databases = COUCHDB_SERVER.databases
46
+ # get all the model files
47
+ Dir[File.join(RAILS_ROOT, CouchRestRails.setup_path,database)].each do |db|
48
+ # check for a directory...
49
+ if File::directory?( db )
50
+ database_name =COUCHDB_CONFIG[:db_prefix] + File.basename(db) +
51
+ COUCHDB_CONFIG[:db_suffix]
52
+ if existing_databases.include?(database_name)
53
+ CouchRest.delete "#{COUCHDB_CONFIG[:host_path]}/#{database_name}"
54
+ resp << "Dropped CouchDB database '#{database_name}'"
55
+ else
56
+ resp << "The CouchDB database '#{database_name}' does not exist"
57
+ end
58
+ end
59
+ end
60
+ resp << "delete complete"
61
+ resp.join("\n")
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,12 @@
1
+ module CouchRestRails
2
+ class Document < CouchRest::ExtendedDocument
3
+
4
+ include Validatable
5
+
6
+ def self.use_database(db)
7
+ db = COUCHDB_CONFIG[:db_prefix] + db.to_s + COUCHDB_CONFIG[:db_suffix]
8
+ # add prefix and suffix to db
9
+ self.database = COUCHDB_SERVER.database(db)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,54 @@
1
+ module CouchRestRails
2
+ module Fixtures
3
+
4
+ extend self
5
+
6
+ def load(database)
7
+ fixture_files = []
8
+ return "Database '#{database}' doesn't exists" unless (database == "*" ||
9
+ File.exist?(File.join(RAILS_ROOT, CouchRestRails.setup_path, database)))
10
+ Dir[File.join(RAILS_ROOT, CouchRestRails.setup_path, database)].each do |db|
11
+ db_name =COUCHDB_CONFIG[:db_prefix] + File.basename( db) +
12
+ COUCHDB_CONFIG[:db_suffix]
13
+ res = CouchRest.get("#{COUCHDB_CONFIG[:host_path]}/#{db_name}") rescue nil
14
+ if res
15
+ db_con = CouchRest.database("#{COUCHDB_CONFIG[:host_path]}/#{db_name}")
16
+ Dir.glob(File.join(RAILS_ROOT, CouchRestRails.fixture_path, "#{database}.yml")).each do |file|
17
+ db_con.bulk_save(YAML::load(ERB.new(IO.read(file)).result).map {|f| f[1]})
18
+ fixture_files << File.basename(file)
19
+ end
20
+ end
21
+ if fixture_files.empty?
22
+ return "No fixtures found in #{CouchRestRails.fixture_path}"
23
+ else
24
+ return "Loaded the following fixture files into '#{db}': #{fixture_files.join(', ')}"
25
+ end
26
+ end
27
+ end
28
+
29
+ def dump(database)
30
+ return "Database '#{database}' doesn't exists" unless (database == "*" ||
31
+ File.exist?(File.join(RAILS_ROOT, CouchRestRails.setup_path, database)))
32
+ Dir[File.join(RAILS_ROOT, CouchRestRails.setup_path, database)].each do |db|
33
+ db_name =COUCHDB_CONFIG[:db_prefix] + File.basename( db) +
34
+ COUCHDB_CONFIG[:db_suffix]
35
+ res = CouchRest.get("#{COUCHDB_CONFIG[:host_path]}/#{db_name}") rescue nil
36
+ if res
37
+ File.open(File.join(RAILS_ROOT, CouchRestRails.fixture_path, "#{database}.yml"), 'w' ) do |file|
38
+ yaml_hash = {}
39
+ db_con = CouchRest.database("#{COUCHDB_CONFIG[:host_path]}/#{db_name}")
40
+ docs = db_con.documents(:include_docs =>true )
41
+ docs["rows"].each { |data|
42
+ doc = data["doc"]
43
+ unless (doc['_id'] =~ /^_design*/) == 0
44
+ doc.delete('_rev')
45
+ yaml_hash[doc['_id']] = doc
46
+ end
47
+ }
48
+ file.write yaml_hash.to_yaml
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,61 @@
1
+ require 'json'
2
+
3
+ module CouchRestRails
4
+ module Lucene
5
+ extend self
6
+
7
+ # Push index/search views to CouchDB
8
+ def push(database, design_doc)
9
+ result = []
10
+ result << "database = #{database} :: design_doc = #{design_doc}"
11
+ db_dir = File.join(RAILS_ROOT, CouchRestRails.setup_path, database)
12
+ return "Database '#{database}' does not exist" unless (database == "*" || File.exist?(db_dir))
13
+
14
+ Dir[db_dir].each do |db|
15
+ return "Lucene directory '#{db}/lucene' does not exist." unless File.exist?("#{db}/lucene")
16
+
17
+ # CouchDB checks
18
+ db_name = COUCHDB_CONFIG[:db_prefix] + File.basename(db) + COUCHDB_CONFIG[:db_suffix]
19
+ begin
20
+ CouchRest.get("#{COUCHDB_CONFIG[:host_path]}/#{db_name}")
21
+ rescue => err
22
+ return "CouchDB database '#{db_name}' does not exist. Create it first. (#{err})"
23
+ end
24
+
25
+ # Update CouchDB designs
26
+ db_con = CouchRest.database("#{COUCHDB_CONFIG[:host_path]}/#{db_name}")
27
+ Dir.glob(File.join(db, 'lucene', design_doc)).each do |doc|
28
+ design_doc_name = File.basename(doc)
29
+
30
+ # Load lucene definition views from FS
31
+ index_views = assemble_lucene(doc)
32
+ couchdb_design = db_con.get("_design/#{design_doc_name}") rescue nil
33
+
34
+ # Update CouchDB's design with fulltext definition files
35
+ if index_views.blank?
36
+ result << "No search was found in #{doc}/#{design_doc_name}"
37
+ else
38
+ # Create the design doc, and/or update fulltext property
39
+ couchdb_design = { '_id' => "_design/#{design_doc_name}" } if couchdb_design.nil?
40
+ couchdb_design['fulltext'] = index_views
41
+
42
+ db_con.save_doc(couchdb_design)
43
+ result << "Added lucene fulltext views to #{design_doc_name}: #{index_views.keys.join(', ')}"
44
+ end
45
+
46
+ end
47
+ end
48
+ result.join("\n")
49
+ end
50
+
51
+ # Assemble views from file-system path lucene_doc_path
52
+ def assemble_lucene(lucene_doc_path)
53
+ search = {}
54
+ Dir.glob(File.join(lucene_doc_path, '*')).each do |search_file|
55
+ search_name = File.basename(search_file).sub(/\.js$/, '')
56
+ search[search_name] = JSON.parse(IO.read(search_file).gsub(/\n/, ''))
57
+ end
58
+ search
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,53 @@
1
+ module CouchRestRails
2
+ module Tests
3
+
4
+ extend self
5
+ mattr_accessor :fixtures_loaded
6
+ self.fixtures_loaded = Set.new
7
+
8
+ def setup(database="*")
9
+ ENV['RAILS_ENV'] = CouchRestRails.test_environment
10
+ unless fixtures_loaded.include?(database)
11
+ CouchRestRails::Database.delete(database)
12
+ CouchRestRails::Database.create(database)
13
+ CouchRestRails::Fixtures.load(database)
14
+ fixtures_loaded << database
15
+ end
16
+ end
17
+
18
+ def reset_fixtures
19
+ CouchRestRails::Database.delete("*") unless fixtures_loaded.empty?
20
+ fixtures_loaded.clear
21
+ end
22
+
23
+ def teardown(database="*")
24
+ ENV['RAILS_ENV'] = CouchRestRails.test_environment
25
+ CouchRestRails::Database.delete(database)
26
+ CouchRestRails::Database.create(database)
27
+ fixtures_loaded.delete(database)
28
+ end
29
+ end
30
+ end
31
+ module Test
32
+ module Unit #:nodoc:
33
+ class TestCase #:nodoc:
34
+ setup :setup_couchdb_fixtures
35
+ teardown :teardown_couchdb_fixtures
36
+
37
+ superclass_delegating_accessor :database
38
+ self.database = nil
39
+
40
+ class << self
41
+ def couchdb_fixtures(*databases)
42
+ self.database = databases.map { |d| d.to_s }
43
+ end
44
+ end
45
+ def setup_couchdb_fixtures
46
+ CouchRestRails::Tests.setup(self.database) unless self.database.nil?
47
+ end
48
+ def teardown_couchdb_fixtures
49
+ CouchRestRails::Tests.teardown(self.database) unless self.database.nil?
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,72 @@
1
+ module CouchRestRails
2
+ module Views
3
+ extend self
4
+
5
+ # Push views to couchdb
6
+ def push(database, design_doc)
7
+ result = []
8
+ result << "database = #{database} :: design_doc = #{design_doc}"
9
+ db_dir = File.join(RAILS_ROOT, CouchRestRails.setup_path, database)
10
+ return "Database directory '#{database}' does not exist" unless (database == "*" || File.exist?(db_dir))
11
+
12
+ Dir[db_dir].each do |db|
13
+ return "Views directory '#{db}/views' does not exist" unless File.exist?("#{db}/views")
14
+
15
+ # CouchDB checks
16
+ db_name = COUCHDB_CONFIG[:db_prefix] + File.basename(db) + COUCHDB_CONFIG[:db_suffix]
17
+ begin
18
+ CouchRest.get("#{COUCHDB_CONFIG[:host_path]}/#{db_name}")
19
+ rescue => err
20
+ return "CouchDB database '#{db_name}' does not exist. Create it first. (#{err})"
21
+ end
22
+
23
+ db_con = CouchRest.database("#{COUCHDB_CONFIG[:host_path]}/#{db_name}")
24
+ Dir.glob(File.join(db, "views", design_doc)).each do |doc|
25
+ design_doc_name = File.basename(doc)
26
+
27
+ # Load views from filesystem & CouchDB
28
+ views = assemble_views(doc)
29
+ couchdb_design = db_con.get("_design/#{design_doc_name}") rescue nil
30
+
31
+ # Update CouchDB's freshly-fetched views, but don't destroy
32
+ # any view not referenced on the FS. Not sure about that part:
33
+ # should we synchronize aggressively?
34
+ views = couchdb_design['views'].merge(views) unless couchdb_design.nil?
35
+
36
+ if views.empty?
37
+ result << "No updatable views in #{doc}/#{File.basename(doc)}"
38
+ else
39
+
40
+ # Create or update the design, then set its views
41
+ if couchdb_design.nil?
42
+ couchdb_design = { '_id' => "_design/#{design_doc_name}",
43
+ 'language' => 'javascript' }
44
+ end
45
+ couchdb_design['views'] = views
46
+
47
+ db_con.save_doc(couchdb_design)
48
+ result << "Added views to #{design_doc_name}: #{views.keys.join(', ')}"
49
+ end
50
+ end
51
+ result << "No views were found in '#{File.join(db, "views", design_doc)}'" if result.empty?
52
+ end
53
+ result.join("\n")
54
+ end
55
+
56
+ # Assemble views from file-system path design_doc_path
57
+ def assemble_views(design_doc_path)
58
+ views = {}
59
+
60
+ Dir.glob(File.join(design_doc_path, '*')).each do |view_folder|
61
+ view = {}
62
+ map_file = File.join(view_folder, 'map.js')
63
+ reduce_file = File.join(view_folder, 'reduce.js')
64
+
65
+ view[:map] = IO.read(map_file) if File.exist?(map_file)
66
+ view[:reduce] = IO.read(reduce_file) if File.exist?(reduce_file) && File.size(reduce_file) > 0
67
+ views[File.basename(view_folder)] = view if view[:map]
68
+ end
69
+ views
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,54 @@
1
+ namespace :couchdb do
2
+
3
+ desc "Create the CouchDB database defined in config/couchdb.yml for the current environment"
4
+ task :create, :database, :needs => :environment do |t, args|
5
+ args.with_defaults(:database => "*")
6
+ puts CouchRestRails::Database.create(args.database)
7
+ end
8
+
9
+ desc "Deletes the CouchDB database for the current RAILS_ENV"
10
+ task :delete, :database, :needs => :environment do |t, args|
11
+ args.with_defaults(:database => "*")
12
+ puts CouchRestRails::Database.delete(args.database)
13
+ end
14
+
15
+ desc "Deletes and recreates the CouchDB database for the current RAILS_ENV"
16
+ task :reset => [:delete, :create]
17
+
18
+ namespace :test do
19
+ desc "Empty the test CouchDB database"
20
+ task :reset do
21
+ `rake RAILS_ENV=test couchdb:delete`
22
+ `rake RAILS_ENV=test couchdb:create`
23
+ end
24
+ end
25
+
26
+ namespace :fixtures do
27
+ desc "Load fixtures into the current environment's CouchDB database"
28
+ task :load, :database, :needs => :environment do |t, args|
29
+ args.with_defaults(:database => "*")
30
+ puts CouchRestRails::Fixtures.load(args.database)
31
+ end
32
+ task :dump, :database, :needs => :environment do |t, args|
33
+ args.with_defaults(:database => "*")
34
+ puts CouchRestRails::Fixtures.dump(args.database)
35
+ end
36
+ end
37
+
38
+ namespace :views do
39
+ desc "Push views into the current environment's CouchDB database"
40
+ task :push, :database, :design_doc, :needs => :environment do |t, args|
41
+ args.with_defaults(:database => "*", :design_doc => "*")
42
+ puts CouchRestRails::Views.push(args.database, args.design_doc)
43
+ end
44
+ end
45
+
46
+ namespace :lucene do
47
+ desc "Push views into the current environment's CouchDB database"
48
+ task :push, :database, :design_doc, :needs => :environment do |t, args|
49
+ args.with_defaults(:database => "*", :design_doc => "*")
50
+ puts CouchRestRails::Lucene.push(args.database, args.design_doc)
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,44 @@
1
+ module Spec
2
+ module Rails
3
+ module Matchers
4
+
5
+ def validate_couchdb_document_format_of(attribute, options)
6
+ return simple_matcher("document model to validate format of :#{attribute} with #{options[:with]}") do |model|
7
+ model.send("#{attribute}=", nil)
8
+ !model.valid? && model.errors.on(attribute)
9
+ end
10
+ end
11
+
12
+ def validate_couchdb_document_length_of(attribute, options)
13
+ return simple_matcher("document model to validate length of :#{attribute} within
14
+ #{options[:maximum] || 0} to #{options[:minimum] || 'infinity'}") do |model|
15
+ if options[:within]
16
+ model.send("#{attribute}=", 'x' * (options[:within].last + 1))
17
+ else
18
+ if options[:maximum]
19
+ model.send("#{attribute}=", 'x' * (options[:maximum] + 1))
20
+ else
21
+ model.send("#{attribute}=", 'x' * (options[:minimum] - 1))
22
+ end
23
+ end
24
+ !model.valid? && model.errors.on(attribute)
25
+ end
26
+ end
27
+
28
+ def validate_couchdb_document_presence_of(attribute)
29
+ return simple_matcher("document model to validate presence of :#{attribute}") do |model|
30
+ model.send("#{attribute}=", nil)
31
+ !model.valid? && model.errors.on(attribute)
32
+ end
33
+ end
34
+
35
+ def validate_couchdb_document_numericality_of(attribute)
36
+ return simple_matcher("document model to validate numericality of :#{attribute}") do |model|
37
+ model.send("#{attribute}=", 'x')
38
+ !model.valid? && model.errors.on(attribute)
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe CouchRestRails::Views do
4
+
5
+ before :each do
6
+ CouchRest.delete(COUCHDB_CONFIG[:full_path]) rescue nil
7
+ end
8
+
9
+ after :all do
10
+ CouchRest.delete(COUCHDB_CONFIG[:full_path]) rescue nil
11
+ end
12
+
13
+ describe '#create' do
14
+
15
+ it 'should create a CouchDB database for the current environment' do
16
+ CouchRestRails::Database.create
17
+ res = CouchRest.get(COUCHDB_CONFIG[:full_path])
18
+ res['db_name'].should == COUCHDB_CONFIG[:database]
19
+ end
20
+
21
+ it 'should do nothing and display a message if the database already exists' do
22
+ CouchRest.database!("#{COUCHDB_CONFIG[:full_path]}")
23
+ res = CouchRestRails::Database.create
24
+ res.should =~ /already exists/i
25
+ end
26
+
27
+ end
28
+
29
+ describe "#delete" do
30
+
31
+ it 'should delete the CouchDB database for the current environment' do
32
+ CouchRest.database!("#{COUCHDB_CONFIG[:full_path]}")
33
+ CouchRestRails::Database.delete
34
+ lambda {CouchRest.get(COUCHDB_CONFIG[:full_path])}.should raise_error('Resource not found')
35
+ end
36
+
37
+ it 'should do nothing and display a message if the database does not exist' do
38
+ res = CouchRestRails::Database.delete
39
+ res.should =~ /does not exist/i
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe CouchRestRails::Document do
4
+
5
+ class CouchRestRailsTestDocument < CouchRestRails::Document
6
+
7
+ end
8
+
9
+ before :each do
10
+ @doc = CouchRestRailsTestDocument.new
11
+ end
12
+
13
+ it "should inherit from CouchRest::ExtendedDocument" do
14
+ CouchRestRails::Document.ancestors.include?(CouchRest::ExtendedDocument).should be_true
15
+ end
16
+
17
+ it "should use the COUCHDB_SERVER constant to define its CouchDB connection" do
18
+ @doc.database.name.should == COUCHDB_CONFIG[:database]
19
+ end
20
+
21
+
22
+ end
@@ -0,0 +1,41 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe CouchRestRails::Fixtures do
4
+
5
+ describe '#blurbs' do
6
+
7
+ it 'should produce an array of text blurbs for testing purposes' do
8
+ CouchRestRails::Fixtures.blurbs.is_a?(Array).should be_true
9
+ end
10
+
11
+ it 'should produce a random text blurb' do
12
+ CouchRestRails::Fixtures.random_blurb.is_a?(String).should be_true
13
+ end
14
+
15
+ end
16
+
17
+ describe '#load' do
18
+
19
+ before :each do
20
+ CouchRest.delete(COUCHDB_CONFIG[:full_path]) rescue nil
21
+ end
22
+
23
+ after :all do
24
+ CouchRest.delete(COUCHDB_CONFIG[:full_path]) rescue nil
25
+ end
26
+
27
+ it "should exit if the database doesn't exist" do
28
+ res = CouchRestRails::Fixtures.load
29
+ res.should =~ /does not exist/i
30
+ end
31
+
32
+ it "should load up the yaml files in CouchRestRails.fixtures_path as documents" do
33
+ db = CouchRest.database!(COUCHDB_CONFIG[:full_path])
34
+ CouchRestRails.fixtures_path = 'vendor/plugins/couchrest-rails/spec/mock/fixtures'
35
+ CouchRestRails::Fixtures.load
36
+ db.documents['rows'].size.should == 10
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe CouchRestRails::Tests do
4
+
5
+ before :each do
6
+ CouchRest.delete(COUCHDB_CONFIG[:full_path]) rescue nil
7
+ CouchRestRails.fixtures_path = 'vendor/plugins/couchrest-rails/spec/mock/fixtures'
8
+ CouchRestRails.views_path = 'vendor/plugins/couchrest-rails/spec/mock/views'
9
+ end
10
+
11
+ after :all do
12
+ CouchRest.delete(COUCHDB_CONFIG[:full_path]) rescue nil
13
+ end
14
+
15
+ describe '#setup' do
16
+
17
+ it 'should delete, add and load fixtures for the test database' do
18
+
19
+ # Create a dirty db first...
20
+ CouchRestRails::Database.create
21
+ db = CouchRest.database(COUCHDB_CONFIG[:full_path])
22
+ CouchRestRails::Fixtures.load
23
+ db.documents['rows'].size.should == 10
24
+
25
+ CouchRestRails::Tests.setup
26
+ db.documents['rows'].size.should == 12 # Includes design docs
27
+ db.view('foos/all')['rows'].size.should == 5
28
+
29
+ end
30
+
31
+ end
32
+
33
+ describe '#teardown' do
34
+
35
+ it 'should delete the test database' do
36
+ CouchRestRails::Tests.setup
37
+ db = CouchRest.database(COUCHDB_CONFIG[:full_path])
38
+ db.documents['rows'].size.should == 12 # Includes design docs
39
+ CouchRestRails::Tests.teardown
40
+ lambda {CouchRest.get(COUCHDB_CONFIG[:full_path])}.should raise_error('Resource not found')
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,25 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe CouchRestRails::Views do
4
+
5
+ before :each do
6
+ CouchRestRails.views_path = 'vendor/plugins/couchrest-rails/spec/mock/views'
7
+ CouchRestRails.fixtures_path = 'vendor/plugins/couchrest-rails/spec/mock/fixtures'
8
+
9
+ end
10
+
11
+ after :all do
12
+ CouchRestRails::Tests.teardown
13
+ end
14
+
15
+ describe '#push' do
16
+
17
+ it "should push the views in CouchRestRails.views_path to a design document for the database" do
18
+ CouchRestRails::Tests.setup # Pushes views
19
+ db = CouchRest.database(COUCHDB_CONFIG[:full_path])
20
+ db.view('foos/all')['rows'].size.should == 5
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'rails_generator'
3
+ require 'rails_generator/scripts/generate'
4
+
5
+ describe 'CouchRestRails' do
6
+
7
+ after :all do
8
+ CouchRest.delete(COUCHDB_CONFIG[:full_path]) rescue nil
9
+ end
10
+
11
+ describe 'plugin installation' do
12
+
13
+ before :all do
14
+ @fake_rails_root = File.join(File.dirname(__FILE__), 'rails_root')
15
+ FileUtils.mkdir_p(@fake_rails_root)
16
+ FileUtils.mkdir_p("#{@fake_rails_root}/config/initializers")
17
+ end
18
+
19
+ after :all do
20
+ FileUtils.rm_rf(@fake_rails_root)
21
+ end
22
+
23
+ it "should generate the necessary files in the host application" do
24
+ Rails::Generator::Scripts::Generate.new.run(
25
+ ['couchrest_rails', 'relax'], :destination => @fake_rails_root)
26
+ Dir.glob(File.join(@fake_rails_root, "**", "*.*")).map {|f| File.basename(f)}.should ==
27
+ ['couchdb.yml', 'couchdb.rb']
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Spec::Rails::Matchers do
4
+
5
+ describe 'validations' do
6
+
7
+ before :all do
8
+ class CouchFoo < CouchRestRails::Document
9
+
10
+ property :something_present
11
+ property :something_long
12
+ property :something_formatted
13
+ property :something_numeric
14
+
15
+ validates_presence_of :something_present
16
+ validates_length_of :something_long, :minimum => 10, :maximum => 50
17
+ validates_format_of :something_formatted, :with => /[A-Z]{3}-[0-9]{3}/
18
+ validates_numericality_of :something_numeric
19
+
20
+ end
21
+ @couch_foo = CouchFoo.new
22
+ end
23
+
24
+ # Use lengthy matcher names so as not to interfere with
25
+ # rspec-on-rails-matchers plugin if present
26
+
27
+ it "should have a matcher for validates_presence_of" do
28
+ @couch_foo.should validate_couchdb_document_presence_of(:something_present)
29
+ end
30
+
31
+ it "should have a matcher for validates_numericality_of" do
32
+ @couch_foo.should validate_couchdb_document_numericality_of(:something_numeric)
33
+ end
34
+
35
+ it "should have a matcher for validates_format_of" do
36
+ @couch_foo.should validate_couchdb_document_format_of(:something_formatted, :with => /[A-Z]{3}-[0-9]{3}/)
37
+ end
38
+
39
+ it "should have a matcher for validates_length_of" do
40
+ @couch_foo.should validate_couchdb_document_length_of(:something_long, :minimum => 10, :maximum => 50)
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,11 @@
1
+ <% def blurb; CouchRestRails::Fixtures.random_blurb; end %>
2
+
3
+ <% 5.times do |i| %>
4
+ record_<%= i %>:
5
+ type: Bar
6
+ question: <%= blurb.split('.').first %>
7
+ answer: <%= blurb %>
8
+ rating: <%= rand(10) %>
9
+ created_at: <%= (Time.now - 3600*rand(10)).utc.strftime('%Y/%m/%d %H:%M:%S +0000') %>
10
+ updated_at: <%= Time.now.utc.strftime('%Y/%m/%d %H:%M:%S +0000') %>
11
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <% def blurb; CouchRestRails::Fixtures.random_blurb; end %>
2
+
3
+ <% 5.times do |i| %>
4
+ record_<%= i %>:
5
+ type: Foo
6
+ title: <%= blurb.split('.').first %>
7
+ body: <%= blurb %>
8
+ tags: <%= blurb.split(' ').sort_by {rand}[0..3].map {|e| e.downcase.gsub(/[^a-z]/, '').strip}.inspect %>
9
+ created_at: <%= (Time.now - 3600*rand(10)).utc.strftime('%Y/%m/%d %H:%M:%S +0000') %>
10
+ updated_at: <%= Time.now.utc.strftime('%Y/%m/%d %H:%M:%S +0000') %>
11
+ <% end %>
@@ -0,0 +1,9 @@
1
+ function(doc) {
2
+ if(doc.type == 'Bar') {
3
+ emit(doc.question, {
4
+ answer : doc.answer,
5
+ rating : doc.rating,
6
+ created_at : doc.created_at
7
+ });
8
+ }
9
+ };
@@ -0,0 +1,9 @@
1
+ function(doc) {
2
+ if(doc.type == 'Foo') {
3
+ emit(doc.created_at, {
4
+ title : doc.title,
5
+ body : doc.body,
6
+ created_at : doc.created_at
7
+ });
8
+ }
9
+ };
@@ -0,0 +1,7 @@
1
+ function(doc) {
2
+ if(doc.type == 'Foo' && doc.tags && doc.tags.length) {
3
+ for(var idx in doc.tags) {
4
+ emit(doc.tags[idx], 1);
5
+ }
6
+ }
7
+ }
@@ -0,0 +1,3 @@
1
+ function(key, values) {
2
+ return sum(values);
3
+ }
@@ -0,0 +1,6 @@
1
+ begin
2
+ require File.dirname(__FILE__) + '/../../../../spec/spec_helper'
3
+ rescue LoadError
4
+ puts "You need to install rspec in your base application"
5
+ exit
6
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), '../lib/couch_rest_tasks')
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: therealadam-couchrest-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Henry Poydar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-22 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Rails plugin for connecting to and working with CouchDB via CouchRest
17
+ email:
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.md
24
+ files:
25
+ - .gitignore
26
+ - MIT-LICENSE
27
+ - README.md
28
+ - Rakefile
29
+ - VERSION
30
+ - generators/couch_rest_rails/USAGE
31
+ - generators/couch_rest_rails/couch_rest_rails_generator.rb
32
+ - generators/couch_rest_rails/templates/couchdb.yml
33
+ - generators/couch_rest_rails/templates/couchdb_initializer.rb
34
+ - init.rb
35
+ - install.rb
36
+ - lib/autotest/discover.rb
37
+ - lib/couch_rest_rails.rb
38
+ - lib/couch_rest_rails/database.rb
39
+ - lib/couch_rest_rails/document.rb
40
+ - lib/couch_rest_rails/fixtures.rb
41
+ - lib/couch_rest_rails/lucene.rb
42
+ - lib/couch_rest_rails/tests.rb
43
+ - lib/couch_rest_rails/views.rb
44
+ - lib/couch_rest_tasks.rb
45
+ - lib/spec/rails/matchers/couch_document_validations.rb
46
+ - spec/lib/couch_rest_rails/database_spec.rb
47
+ - spec/lib/couch_rest_rails/document_spec.rb
48
+ - spec/lib/couch_rest_rails/fixtures_spec.rb
49
+ - spec/lib/couch_rest_rails/tests_spec.rb
50
+ - spec/lib/couch_rest_rails/views_spec.rb
51
+ - spec/lib/couch_rest_rails_spec.rb
52
+ - spec/matchers/couch_document_validations_spec.rb
53
+ - spec/mock/fixtures/bars.yml
54
+ - spec/mock/fixtures/foos.yml
55
+ - spec/mock/views/bars/all/map.js
56
+ - spec/mock/views/foos/all/map.js
57
+ - spec/mock/views/foos/tags/map.js
58
+ - spec/mock/views/foos/tags/reduce.js
59
+ - spec/spec_helper.rb
60
+ - tasks/couch_rest_rails_tasks.rake
61
+ - uninstall.rb
62
+ has_rdoc: true
63
+ homepage: http://github.com/therealadam/couchrest-rails
64
+ post_install_message:
65
+ rdoc_options:
66
+ - --charset=UTF-8
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ version:
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.2.0
85
+ signing_key:
86
+ specification_version: 2
87
+ summary: Make working with CouchDB and Rails even better.
88
+ test_files:
89
+ - spec/lib/couch_rest_rails/database_spec.rb
90
+ - spec/lib/couch_rest_rails/document_spec.rb
91
+ - spec/lib/couch_rest_rails/fixtures_spec.rb
92
+ - spec/lib/couch_rest_rails/tests_spec.rb
93
+ - spec/lib/couch_rest_rails/views_spec.rb
94
+ - spec/lib/couch_rest_rails_spec.rb
95
+ - spec/matchers/couch_document_validations_spec.rb
96
+ - spec/spec_helper.rb