therealadam-couchrest-rails 0.1.1

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.
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