topfunky (owner)

Forks

Revisions

gist: 10636 Download_button fork
public
Public Clone URL: git://gist.github.com/10636.git
Embed All Files: show embed
basic_model.rb #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# NOTE: See further development and a gem at http://github.com/topfunky/basic_model
 
require 'couchrest'
 
##
# 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
 
  attr_accessor :attributes
 
  def self.db(database_name)
    puts "Getting #{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)
    # Synchronize views
    file_manager = CouchRest::FileManager.new(File.basename(full_url_to_database))
    file_manager.push_views(File.join(Rails.root, "couchdb_views"))
 
    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)
    self.class.db(@database_name).save(@attributes)
  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