Permalink
Browse files

First import

  • Loading branch information...
topfunky committed Sep 14, 2008
0 parents commit 435f72383a5610c8c4a4a3cd46008db3397300a6
Showing with 322 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +4 −0 History.txt
  3. +7 −0 Manifest.txt
  4. +61 −0 README.txt
  5. +12 −0 Rakefile
  6. +33 −0 basic_model.gemspec
  7. +1 −0 init.rb
  8. +167 −0 lib/basic_model.rb
  9. +12 −0 lib/duck_punches/date.rb
  10. +12 −0 lib/duck_punches/date_time.rb
  11. +12 −0 lib/duck_punches/time.rb
  12. 0 test/test_basic_model.rb
@@ -0,0 +1 @@
+.DS_Store
@@ -0,0 +1,4 @@
+=== 0.1.0 / 2008-09-13
+
+* First release
+
@@ -0,0 +1,7 @@
+History.txt
+Manifest.txt
+README.txt
+Rakefile
+init.rb
+lib/basic_model.rb
+test/test_basic_model.rb
@@ -0,0 +1,61 @@
+= BasicModel
+
+* http://github.com/topfunky/basic_model (url)
+
+== DESCRIPTION:
+
+A very thin wrapper around CouchRest, for use with CouchDB.
+
+== FEATURES/PROBLEMS:
+
+* Usable with Rails in place of ActiveRecord (for forms and URLs)
+
+== SYNOPSIS:
+
+Subclass BasicModel. See the rdoc in basic_model.rb for examples.
+
+ class Event < BasicModel
+
+ def default_attributes
+ {:some => "defaults"}
+ end
+
+ def on_update
+ # Process @attributes before saving
+ end
+
+ end
+
+== REQUIREMENTS:
+
+* jchris-couchrest gem
+* RestClient from Adam Wiggins of Heroku
+
+== INSTALL:
+
+* sudo gem install jchris-couchrest -a http://gems.github.com
+
+== LICENSE:
+
+(The MIT License)
+
+Copyright (c) 2008 Topfunky Corporation
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,12 @@
+# -*- ruby -*-
+
+require 'rubygems'
+require 'hoe'
+require './lib/basic_model.rb'
+
+Hoe.new('BasicModel', BasicModel::VERSION) do |p|
+ # p.rubyforge_name = 'BasicModelx' # if different than lowercase project name
+ p.developer('Geoffrey Grosenbach', 'boss@topfunky.com')
+end
+
+# vim: syntax=Ruby
@@ -0,0 +1,33 @@
+Gem::Specification.new do |s|
+ s.name = %q{BasicModel}
+ s.version = "0.1.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Geoffrey Grosenbach"]
+ s.date = %q{2008-09-13}
+ s.description = %q{A very thin wrapper around CouchRest, for use with CouchDB.}
+ s.email = ["boss@topfunky.com"]
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
+ s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "init.rb", "lib/basic_model.rb", "test/test_basic_model.rb"]
+ s.has_rdoc = true
+ s.homepage = %q{http://github.com/topfunky/basic_model (url)}
+ s.rdoc_options = ["--main", "README.txt"]
+ s.require_paths = ["lib"]
+ s.rubyforge_project = %q{basicmodel}
+ s.rubygems_version = %q{1.2.0}
+ s.summary = %q{A very thin wrapper around CouchRest, for use with CouchDB.}
+ s.test_files = ["test/test_basic_model.rb"]
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 2
+
+ if current_version >= 3 then
+ s.add_development_dependency(%q<hoe>, [">= 1.7.0"])
+ else
+ s.add_dependency(%q<hoe>, [">= 1.7.0"])
+ end
+ else
+ s.add_dependency(%q<hoe>, [">= 1.7.0"])
+ end
+end
@@ -0,0 +1 @@
+require File.dirname(__FILE__) + '/lib/basic_model'
@@ -0,0 +1,167 @@
+$: << File.dirname(__FILE__)
+require 'couchrest'
+require 'duck_punches/time'
+require 'duck_punches/date_time'
+require 'duck_punches/date'
+
+##
+# A minimal class to help use CouchDB and CouchRest with Rails.
+#
+# Provides dot notation access for all attributes, one level deep.
+#
+# note.title
+# # Instead of
+# note['title']
+#
+# You should subclass this so routes are properly generated when making forms.
+#
+# class Note < BasicModel; end
+#
+# note = Note.new('my_db_name')
+#
+# note = Note.find('my_db_name', '4b463a09321e223b5a7aa1034e28e125')
+# result = note.save(params[:note])
+#
+# notes = Note.view('my_db_name', 'notes/by_title-map', :key => 'Restaurant')
+# results = notes.rows
+#
+# Subclasses can implement two methods:
+#
+# default_attributes() # Should return a hash that all instances will be
+# # initialized with.
+# on_update() # Called just before a model is written to the DB.
+
+class BasicModel
+ VERSION = '0.1.0'
+
+ attr_accessor :attributes
+
+ def self.db(database_name)
+ full_url_to_database = database_name
+ if full_url_to_database !~ /^http:\/\//
+ full_url_to_database = "http://localhost:5984/#{database_name}"
+ end
+ database = CouchRest.database!(full_url_to_database)
+ if Rails.env == 'development'
+ # Synchronize views in development.
+ # Assumes existence of "couchdb_views" directory.
+ file_manager = CouchRest::FileManager.new(File.basename(full_url_to_database))
+ file_manager.push_views(File.join(Rails.root, "couchdb_views"))
+ end
+ database
+ end
+
+ def initialize(database_name, attributes={})
+ @database_name = database_name
+ @attributes = default_attributes.merge(attributes)
+ end
+
+ ##
+ # To be overridden by subclasses.
+
+ def default_attributes
+ {}
+ end
+
+ ##
+ # Finds a document by ID and turns it into something
+ # usable with Rails.
+ #
+ # note = Note.find('my_db_name', '283934927362')
+ # note.id
+ # note._rev
+ # note.new_record?
+ # note.title # Any field from the record
+
+ def self.find(database_name, id)
+ new(database_name, self.db(database_name).get(id))
+ end
+
+ ##
+ # Takes a set of results from a CouchRest view call and turns the
+ # rows into Rails-friendly objects.
+ #
+ # notes = Note.view('my_db_name', 'notes/by_title')
+ # notes.rows.each {|row| row.id ... }
+
+ def self.view(database_name, view_name, options={})
+ results = new(database_name, self.db(database_name).view(view_name, options))
+ results.rows.each_with_index do |row, index|
+ results.rows[index] = new(database_name, row['value'])
+ end
+ results
+ end
+
+ ##
+ # Merges attributes with the existing record and saves to CouchDB.
+ #
+ # If attributes has an "attachment" field, it will be read and
+ # formatted for inclusion as a CouchDB attachment to the document.
+
+ def save(attributes={})
+ @attributes = @attributes.merge(attributes)
+ handle_attachments
+ self.type = self.class.name
+ if new_record?
+ self.created_at = Time.now
+ end
+ self.updated_at = Time.now
+ self.on_update if self.respond_to?(:on_update)
+ result = self.class.db(@database_name).save(@attributes)
+ self._rev = result['rev']
+ self
+ end
+
+ ##
+ # Returns the ID so Rails can use it for forms.
+
+ def id
+ _id rescue nil
+ end
+ alias_method :to_param, :id
+
+ def new_record?
+ (_rev).nil?
+ rescue NameError
+ true
+ end
+
+ ##
+ # Handles getters and setters for the first level of the hash.
+ #
+ # record._rev
+ # record.title
+ # record.title = "Streetside bratwurst vendor"
+
+ def method_missing(method_symbol, *arguments)
+ method_name = method_symbol.to_s
+
+ case method_name[-1..-1]
+ when "="
+ @attributes[method_name[0..-2]] = arguments.first
+ when "?"
+ @attributes[method_name[0..-2]] == true
+ else
+ # Returns nil on failure so forms will work
+ @attributes.has_key?(method_name) ? @attributes[method_name] : nil
+ end
+ end
+
+ private
+
+ def handle_attachments
+ # Save an attachment
+ if @attributes['attachment'].is_a?(ActionController::UploadedTempfile)
+ attachment = @attributes.delete("attachment")
+ @attributes["_attachments"] ||= {}
+ filename = File.basename(attachment.original_filename)
+ @attributes["_attachments"][filename] = {
+ "content_type" => attachment.content_type,
+ "data" => attachment.read
+ }
+ else
+ @attributes.delete("attachment")
+ end
+ end
+
+end
@@ -0,0 +1,12 @@
+class Date
+ def to_json(*a)
+ {
+ 'json_class' => self.class.name,
+ 'data' => self.strftime("%Y/%m/%d")
+ }.to_json(*a)
+ end
+
+ def self.json_create(o)
+ parse(*o['data'])
+ end
+end
@@ -0,0 +1,12 @@
+class DateTime
+ def to_json(*a)
+ {
+ 'json_class' => self.class.name,
+ 'data' => self.strftime("%Y/%m/%d %H:%M:%S %z")
+ }.to_json(*a)
+ end
+
+ def self.json_create(o)
+ parse(*o['data'])
+ end
+end
@@ -0,0 +1,12 @@
+class Time
+ def to_json(*a)
+ {
+ 'json_class' => self.class.name,
+ 'data' => self.strftime("%Y/%m/%d %H:%M:%S %z")
+ }.to_json(*a)
+ end
+
+ def self.json_create(o)
+ parse(*o['data'])
+ end
+end
No changes.

0 comments on commit 435f723

Please sign in to comment.