lib/exegesis/design.rb in mattly-exegesis-0.0.10 vs lib/exegesis/design.rb in mattly-exegesis-0.2.0

- old
+ new

@@ -1,84 +1,155 @@ -$:.unshift File.dirname(__FILE__) -require 'design/design_docs' - +require 'pathname' module Exegesis class Design - include Exegesis::Design::DesignDocs + include Exegesis::Document - attr_accessor :database + def self.design_directory= dir + @design_directory = Pathname.new(dir) + end - def initialize(db) - @database = db + def self.design_directory + @design_directory ||= Pathname.new("designs/#{design_name}") end - def self.use_design_doc_name name - @design_doc_name = name.to_s + def self.design_name= n + @design_name = n.to_s end - def self.design_doc_name - @design_doc_name ||= name.to_s.sub(/(Design)$/,'').downcase + def self.design_name + @design_name ||= name.scan(/^(?:[A-Za-z0-9\:]+::)([A-Za-z0-9]+)Design$/).first.first.downcase end - def design_doc_name - self.class.design_doc_name + def self.compose_canonical + Dir[design_directory + 'views' + '**/*.js'].each do |view_func| + path = view_func.split('/') + func = path.pop.sub(/\.js$/,'') + name = path.pop + canonical_design['views'][name] ||= {} + canonical_design['views'][name][func] = File.read(view_func) + end end - def get(id) - doc = Exegesis::Document.instantiate database.get(id) - doc.database = self.database - doc + def self.canonical_design + @canonical_design ||= { + '_id' => "_design/#{design_name}", + 'views' => {} + } end - def parse_opts(opts={}) - if opts[:key] - case opts[:key] - when Range - range = opts.delete(:key) - opts.update({:startkey => range.first, :endkey => range.last}) - when Array - if opts[:key].any?{|v| v.kind_of?(Range) } - key = opts.delete(:key) - opts[:startkey] = key.map {|v| v.kind_of?(Range) ? v.first : v } - opts[:endkey] = key.map {|v| v.kind_of?(Range) ? v.last : v } + def self.view name, default_options={} + define_method name do |key, *opts| + view name, key, opts.first, default_options + end + end + + def self.docs name, default_options={} + default_options = {:include_docs => true, :reduce => false}.merge(default_options) + define_method name do |*opts| + key = opts.shift + options = parse_opts key, opts.first, default_options + response = call_view name, options + ids = [] + response.inject([]) do |memo, doc| + unless ids.include?(doc['id']) + ids << doc['id'] + memo << Exegesis.instantiate(doc['doc'], database) end + memo end - elsif opts[:keys] && opts[:keys].empty? - opts.delete(:keys) end - - opts end - def view view_name, opts={} - opts = parse_opts opts - return [] unless opts[:key] || opts[:startkey] || opts[:keys] || opts[:all] - opts.delete(:all) - database.view("#{design_doc_name}/#{view_name}", opts)['rows'] + def self.hash name, default_options={} + default_options = {:group => true}.merge(default_options) + view_name = default_options.delete(:view) || name + define_method name do |*opts| + key = opts.shift + options = parse_opts key, opts.first, default_options + options.delete(:group) if options[:key] + + response = call_view view_name, options + if response.size == 1 && response.first['key'].nil? + response.first['value'] + else + response.inject({}) do |memo, row| + if ! memo.has_key?(row['key']) + memo[row['key']] = row['value'] + end + memo + end + end + end end - def docs_for view_name, opts={} - response = view view_name, opts.update({:include_docs => true}) - response.map do |doc| - model = Exegesis::Document.instantiate doc['doc'] - model.database = database - model + + def initialize db + begin + super db.get("_design/#{design_name}"), db + rescue RestClient::ResourceNotFound + db.put("_design/#{design_name}", self.class.canonical_design) + retry end + unless self['views'] == self.class.canonical_design['views'] + self['views'].update(self.class.canonical_design['views']) + save + end end - def values_for view_name, opts={} - response = view view_name, opts - response.map {|row| row['value'] } + def view name, key=nil, opts={} + call_view name, parse_opts(key, opts) end - def keys_for view_name, opts={} - response = view view_name, opts - response.map {|row| row['key'] } + def call_view name, opts={} + url = "_design/#{design_name}/_view/#{name}" + database.raw_get(url, opts)['rows'] end - def ids_for view_name, opts={} - response = view view_name, opts - response.map {|row| row['id'] } + def design_name + self.class.design_name + end + + def parse_opts key, opts={}, defaults={} + opts = straighten_args key, opts, defaults + parse_key opts + parse_keys opts + parse_range opts + opts + end + + private + + def straighten_args key, opts, defaults + opts ||= {} + if key.is_a?(Hash) + opts = key + elsif ! key.nil? + opts[:key] = key + end + defaults.merge(opts) + end + + def parse_key opts + if opts[:key] + if opts[:key].is_a?(Range) + range = opts.delete(:key) + opts.update({:startkey => range.first, :endkey => range.last}) + elsif opts[:key].is_a?(Array) && opts[:key].any?{|v| v.kind_of?(Range) } + key = opts.delete(:key) + opts[:startkey] = key.map {|v| v.kind_of?(Range) ? v.first : v } + opts[:endkey] = key.map {|v| v.kind_of?(Range) ? v.last : v } + end + end + end + + def parse_keys opts + opts.delete(:keys) if opts[:keys] && opts[:keys].empty? + end + + def parse_range opts + if opts[:startkey] || opts[:endkey] + raise ArgumentError, "both a startkey and endkey must be specified if either is" unless opts[:startkey] && opts[:endkey] + end end end end \ No newline at end of file