diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 77349208..43a0bf71 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -24,11 +24,11 @@ jobs: - ruby: "jruby" gemfile: "active_support_8_0" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up CouchDB - uses: cobot/couchdb-action@v4 + uses: cobot/couchdb-action@v5 with: - couchdb version: "2.3.1" + couchdb-version: "2.3.1" - name: Set up Ruby uses: ruby/setup-ruby@v1 with: @@ -36,3 +36,5 @@ jobs: bundler-cache: true - name: Run tests run: bundle exec rake spec_ci + env: + DATABASE: http://admin:admin@localhost:5984/couch_potato_test diff --git a/CHANGES.md b/CHANGES.md index c5865d55..8ee08650 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ ## Changes +# 1.19.0 / rspec-matchers 4.2.0 + +- add `single_design_document` config option +- remove support for lists and lib + # 1.18.0 - add testing Rails 7.2/8 on CI diff --git a/README.md b/README.md index c10e67ac..340c32b6 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,12 @@ Another switch allows you to store each CouchDB view in its own design document. CouchPotato::Config.split_design_documents_per_view = true ``` +With the following switch, couch potato only creates a single design document for all views: + +```ruby +CouchPotato::Config.single_design_document = true +``` + If you are using more than one database from your app, you can create aliases: ```ruby @@ -95,6 +101,7 @@ Create a `config/couchdb.yml`: default: &default split_design_documents_per_view: true # optional, default is false digest_view_names: true # optional, default is false + single_design_document: true # optional, default is false default_language: :erlang # optional, default is javascript database_host: "http://127.0.0.1:5984" @@ -299,7 +306,7 @@ user.valid? # => false user.errors[:name] # => ['can't be blank'] ``` -#### Finding stuff / views / lists +#### Finding stuff / views In order to find data in your CouchDB you have to create a [view](http://books.couchdb.org/relax/design-documents/views) first. Couch Potato offers you to create and manage those views for you. All you have to do is declare them in your classes: @@ -403,14 +410,6 @@ class User end ``` -commonJS modules can also be used in custom views: - -```ruby -class User - view :all, :map => "function(doc) { emit(null, require("views/lib/test").test)}", :lib => {:test => "exports.test = 'test'"}, :include_docs => true, :type => :custom -end -``` - If you don't want the results to be converted into models the raw view is your friend: ```ruby @@ -450,49 +449,6 @@ You can pass in your own view specifications by passing in `:type => MyViewSpecC If turned on, Couch Potato will append an MD5 digest of the map function to each view name. This makes sure (together with split_design_documents_per_view) that no views/design documents are ever updated. Instead, new ones are created. Since reindexing can take a long time once your database is larger, you want to avoid blocking your app while CouchDB is busy. Instead, you create a new view, warm it up, and only then start using it. -##### Lists - -CouchPotato also supports [CouchDB lists](http://books.couchdb.org/relax/design-documents/lists). With lists you can process the result of a view query with another JavaScript function. This can be useful for example if you want to filter your results, or add some data to each document. - -Defining a list works similarly to views: - -```ruby -class User - include CouchPotato::Persistence - - property :first_name - view :with_full_name, key: first_namne, list: :add_last_name - view :all, key: :first_name - - list :add_last_name, <<-JS - function(head, req) { - var row; - send('{"rows": ['); - while(row = getRow()) { - row.doc.name = row.doc.first_name + ' doe'; - send(JSON.stringify(row)); - }; - send(']}'); - } - JS -end - -CouchPotato.database.save User.new(first_name: 'joe') -CouchPotato.database.view(User.with_full_name).first.name # => 'joe doe' -``` - -You can also pass in the list at query time: - -```ruby -CouchPotato.database.view(User.all(list: :add_last_name)) -``` - -And you can pass parameters to the list: - -```ruby -CouchPotato.database.view(User.all(list: :add_last_name, list_params: {filter: '*'})) -``` - #### Associations Not supported. Not sure if they ever will be. You can implement those yourself using views and custom methods on your models. diff --git a/lib/couch_potato.rb b/lib/couch_potato.rb index 9da59fd7..d25275a0 100644 --- a/lib/couch_potato.rb +++ b/lib/couch_potato.rb @@ -9,9 +9,10 @@ CouchRest.decode_json_objects = true module CouchPotato - Config = Struct.new(:database_host, :database_name, :digest_view_names, + Config = Struct.new(:database_host, :database_name, :digest_view_names, :single_design_document, :split_design_documents_per_view, :default_language, :additional_databases).new Config.split_design_documents_per_view = false + Config.single_design_document = false Config.digest_view_names = false Config.default_language = :javascript Config.database_host = 'http://127.0.0.1:5984' @@ -29,6 +30,7 @@ def self.configure(config) Config.database_host = config['database_host'] if config['database_host'] Config.additional_databases = config['additional_databases'].stringify_keys if config['additional_databases'] Config.split_design_documents_per_view = config['split_design_documents_per_view'] if config['split_design_documents_per_view'] + Config.single_design_document = config['single_design_document'] if config['single_design_document'] Config.digest_view_names = config['digest_view_names'] if config['digest_view_names'] Config.default_language = config['default_language'] if config['default_language'] end @@ -40,6 +42,12 @@ def self.models @models end + # returns all the classes that include the CouchPotato::View::CustomViews module + def self.views + @views ||= [] + @views + end + # Returns a database instance which you can then use to create objects and query views. You have to set the CouchPotato::Config.database_name before this works. def self.database Thread.current[:__couch_potato_database] ||= Database.new(couchrest_database) diff --git a/lib/couch_potato/database.rb b/lib/couch_potato/database.rb index f0a880d9..d0a17e83 100644 --- a/lib/couch_potato/database.rb +++ b/lib/couch_potato/database.rb @@ -273,8 +273,6 @@ def raw_view(spec) map: spec.map_function, reduce: spec.reduce_function } }, - ({ spec.list_name => spec.list_function } unless spec.list_name.nil?), - spec.lib, spec.language ).query_view!(spec.view_parameters) end diff --git a/lib/couch_potato/persistence.rb b/lib/couch_potato/persistence.rb index 9cef8a1b..f6629461 100644 --- a/lib/couch_potato/persistence.rb +++ b/lib/couch_potato/persistence.rb @@ -12,16 +12,20 @@ require File.dirname(__FILE__) + '/persistence/revisions' require File.dirname(__FILE__) + '/forbidden_attributes_protection' require File.dirname(__FILE__) + '/view/custom_views' -require File.dirname(__FILE__) + '/view/lists' require File.dirname(__FILE__) + '/view/view_query' module CouchPotato module Persistence + module TrackModels + def inherited(child) + super + CouchPotato.models << child + end + end def self.included(base) #:nodoc: - base.send :include, Properties, Callbacks, Json, CouchPotato::View::CustomViews, - CouchPotato::View::Lists + base.send :include, Properties, Callbacks, Json, CouchPotato::View::CustomViews base.send :include, DirtyAttributes, GhostAttributes, Attachments base.send :include, MagicTimestamps, ActiveModelCompliance, ForbiddenAttributesProtection, Revisions @@ -31,9 +35,8 @@ def self.included(base) #:nodoc: alias_method :id, :_id alias_method :id=, :_id= - def self.inherited(child) - super - CouchPotato.models << child + class << self + prepend TrackModels end end diff --git a/lib/couch_potato/rspec/matchers.rb b/lib/couch_potato/rspec/matchers.rb index 1d900cd7..5dd65aa2 100644 --- a/lib/couch_potato/rspec/matchers.rb +++ b/lib/couch_potato/rspec/matchers.rb @@ -3,7 +3,6 @@ require 'couch_potato/rspec/matchers/map_to_matcher' require 'couch_potato/rspec/matchers/reduce_to_matcher' require 'couch_potato/rspec/matchers/map_reduce_to_matcher' -require 'couch_potato/rspec/matchers/list_as_matcher' module RSpec module Matchers @@ -19,10 +18,6 @@ def rereduce(keys, values) CouchPotato::RSpec::ReduceToProxy.new(keys, values, true) end - def list(results) - CouchPotato::RSpec::ListAsProxy.new(results) - end - def map_reduce(*docs) CouchPotato::RSpec::MapReduceToProxy.new(docs) end diff --git a/lib/couch_potato/rspec/matchers/list_as_matcher.rb b/lib/couch_potato/rspec/matchers/list_as_matcher.rb deleted file mode 100644 index 089fa0c0..00000000 --- a/lib/couch_potato/rspec/matchers/list_as_matcher.rb +++ /dev/null @@ -1,54 +0,0 @@ -module CouchPotato - module RSpec - class ListAsProxy - def initialize(results_ruby) - @results_ruby = results_ruby - end - - def as(expected_ruby) - ListAsMatcher.new(expected_ruby, @results_ruby) - end - end - - class ListAsMatcher - include ::RSpec::Matchers::Composable - - def initialize(expected_ruby, results_ruby) - @expected_ruby = expected_ruby - @results_ruby = results_ruby - end - - def matches?(view_spec) - js = <<-JS - (function() { - var results = #{@results_ruby.to_json}; - var listed = ''; - var list = #{view_spec.list_function}; - - var getRow = function() { - return results.rows.shift(); - }; - var send = function(text) { - listed = listed + text; - }; - list(); - return JSON.stringify(JSON.parse(listed)); - })() - - JS - @actual_ruby = JSON.parse(ExecJS.eval(js)) - - values_match? @expected_ruby, @actual_ruby - end - - def failure_message - "Expected to list as #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}." - end - - def failure_message_when_negated - "Expected to not list as #{@expected_ruby.inspect} but did." - end - - end - end -end diff --git a/lib/couch_potato/rspec/matchers/map_reduce_to_matcher.rb b/lib/couch_potato/rspec/matchers/map_reduce_to_matcher.rb index 5904321e..b2e42231 100644 --- a/lib/couch_potato/rspec/matchers/map_reduce_to_matcher.rb +++ b/lib/couch_potato/rspec/matchers/map_reduce_to_matcher.rb @@ -72,7 +72,6 @@ def matches?(view_spec) var options = #{@options.to_json}; var map = #{view_spec.map_function}; var reduce = #{view_spec.reduce_function}; - var lib = #{view_spec.respond_to?(:lib) && view_spec.lib.to_json}; var collate = (function() { var module = {exports: {}}; var exports = module.exports; eval(#{File.read(File.expand_path(File.dirname(__FILE__) + '/../../../../vendor/pouchdb-collate/pouchdb-collate.js')).to_json}); return module.exports.collate;})(); // Map the input docs @@ -80,7 +79,7 @@ def matches?(view_spec) var module = {exports: {}}; var exports = module.exports; var pathArray = modulePath.split("/").slice(2); - var result = lib; + var result = {}; for (var i in pathArray) { result = result[pathArray[i]]; } diff --git a/lib/couch_potato/rspec/matchers/map_to_matcher.rb b/lib/couch_potato/rspec/matchers/map_to_matcher.rb index c12404d6..10899a5c 100644 --- a/lib/couch_potato/rspec/matchers/map_to_matcher.rb +++ b/lib/couch_potato/rspec/matchers/map_to_matcher.rb @@ -26,13 +26,12 @@ def matches?(view_spec) (function() { var doc = #{@input_ruby.to_json}; var map = #{view_spec.map_function}; - var lib = #{view_spec.respond_to?(:lib) && view_spec.lib.to_json}; var result = []; var require = function(modulePath) { var module = {exports: {}}; var exports = module.exports; var pathArray = modulePath.split("/").slice(2); - var result = lib; + var result = {}; for (var i in pathArray) { result = result[pathArray[i]]; } diff --git a/lib/couch_potato/version.rb b/lib/couch_potato/version.rb index 031699c3..5e22f5fc 100644 --- a/lib/couch_potato/version.rb +++ b/lib/couch_potato/version.rb @@ -1,4 +1,4 @@ module CouchPotato - VERSION = '1.18.0'.freeze - RSPEC_VERSION = '4.1.0'.freeze + VERSION = '1.19.0'.freeze + RSPEC_VERSION = '4.2.0'.freeze end diff --git a/lib/couch_potato/view/base_view_spec.rb b/lib/couch_potato/view/base_view_spec.rb index 9ed55ac2..81c3e515 100644 --- a/lib/couch_potato/view/base_view_spec.rb +++ b/lib/couch_potato/view/base_view_spec.rb @@ -3,7 +3,7 @@ module CouchPotato module View class BaseViewSpec - attr_reader :reduce_function, :lib, :list_name, :list_function, :design_document, :view_name, :klass, :options, :language + attr_reader :reduce_function, :design_document, :view_name, :klass, :options, :language attr_accessor :view_parameters private :klass, :options @@ -11,7 +11,7 @@ class BaseViewSpec def initialize(klass, view_name, options, view_parameters) normalized_view_parameters = ViewParameters.normalize_view_parameters view_parameters - @list_name = normalized_view_parameters.delete(:list) || options[:list] + @language = options[:language] || Config.default_language assert_valid_view_parameters normalized_view_parameters @@ -19,16 +19,12 @@ def initialize(klass, view_name, options, view_parameters) @options = options @view_name = compute_view_name(view_name, options.key?(:digest_view_name) ? options[:digest_view_name] : Config.digest_view_names) - @design_document = translate_to_design_doc_name(klass.to_s, @view_name, @list_name) - @list_params = normalized_view_parameters.delete :list_params - - @list_function = klass.lists(@list_name) if @list_name + @design_document = design_doc_name @view_parameters = {} %i[group include_docs descending group_level limit].each do |key| @view_parameters[key] = options[key] if options.include?(key) end @view_parameters.merge!(normalized_view_parameters) - @view_parameters.merge!(@list_params) if @list_params end def process_results(results) @@ -42,10 +38,16 @@ def process_results(results) private def compute_view_name(view_name, digest) + name = if CouchPotato::Config.single_design_document + "#{translate_to_design_doc_name(klass.to_s, view_name)}-#{view_name}" + else + view_name + end + if digest - "#{view_name}-#{Digest::MD5.hexdigest(map_function + reduce_function.to_s)}" + "#{name}-#{Digest::MD5.hexdigest(map_function + reduce_function.to_s)}" else - view_name + name end end @@ -56,10 +58,18 @@ def assert_valid_view_parameters(params) end def valid_view_parameters - %w[list_params key keys startkey startkey_docid endkey endkey_docid limit stale descending skip group group_level reduce include_docs inclusive_end] + %w[key keys startkey startkey_docid endkey endkey_docid limit stale descending skip group group_level reduce include_docs inclusive_end] + end + + def design_doc_name + if CouchPotato::Config.single_design_document + 'couch_potato' + else + translate_to_design_doc_name(klass.to_s, view_name) + end end - def translate_to_design_doc_name(klass_name, view_name, list_name) + def translate_to_design_doc_name(klass_name, view_name) klass_name = klass_name.dup klass_name.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2') klass_name.gsub!(/([a-z\d])([A-Z])/, '\1_\2') @@ -68,7 +78,6 @@ def translate_to_design_doc_name(klass_name, view_name, list_name) if CouchPotato::Config.split_design_documents_per_view doc_name += "_view_#{view_name}" if view_name.present? - doc_name += "_list_#{list_name}" if list_name.present? end doc_name end diff --git a/lib/couch_potato/view/custom_view_spec.rb b/lib/couch_potato/view/custom_view_spec.rb index ecc27813..cbbefe79 100644 --- a/lib/couch_potato/view/custom_view_spec.rb +++ b/lib/couch_potato/view/custom_view_spec.rb @@ -13,10 +13,6 @@ def reduce_function options[:reduce] end - def lib - options[:lib] - end - def view_parameters {:include_docs => options[:include_docs] || false}.merge(super) end diff --git a/lib/couch_potato/view/custom_views.rb b/lib/couch_potato/view/custom_views.rb index 1b749945..85ca77f5 100644 --- a/lib/couch_potato/view/custom_views.rb +++ b/lib/couch_potato/view/custom_views.rb @@ -9,9 +9,21 @@ module CouchPotato module View module CustomViews + module TrackViews + def inherited(child) + super + CouchPotato.views << child + end + end def self.included(base) #:nodoc: base.extend ClassMethods + CouchPotato.views << base + base.class_eval do + class << self + prepend TrackViews + end + end end module ClassMethods diff --git a/lib/couch_potato/view/flex_view_spec.rb b/lib/couch_potato/view/flex_view_spec.rb index bb49b236..d90566ff 100644 --- a/lib/couch_potato/view/flex_view_spec.rb +++ b/lib/couch_potato/view/flex_view_spec.rb @@ -96,7 +96,7 @@ def initialize(klass, view_name, options, view_parameters) end delegate :view_name, :view_parameters, :design_document, :map_function, - :reduce_function, :list_name, :lib, :language, to: :view_spec_delegate + :reduce_function, :language, to: :view_spec_delegate def process_results(results) results = Results.new(results) diff --git a/lib/couch_potato/view/lists.rb b/lib/couch_potato/view/lists.rb deleted file mode 100644 index d0e0ce5c..00000000 --- a/lib/couch_potato/view/lists.rb +++ /dev/null @@ -1,23 +0,0 @@ -module CouchPotato - module View - module Lists - def self.included(base) - base.send :extend, ClassMethods - end - - module ClassMethods - def list(name, function) - lists[name] = function - end - - def lists(name = nil) - if name.nil? - @lists ||= {} - else - (@lists && @lists[name]) || (superclass.lists(name) if superclass.respond_to?(:lists)) - end - end - end - end - end -end \ No newline at end of file diff --git a/lib/couch_potato/view/raw_view_spec.rb b/lib/couch_potato/view/raw_view_spec.rb index 4a4b7fd1..59bb731a 100644 --- a/lib/couch_potato/view/raw_view_spec.rb +++ b/lib/couch_potato/view/raw_view_spec.rb @@ -1,6 +1,6 @@ module CouchPotato module View - # A view for custom map/reduce functions that returns the raw data fromcouchdb + # A view for custom map/reduce functions that returns the raw data from couchdb # # example: # view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw, :reduce => nil @@ -10,7 +10,7 @@ module View # view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw, :results_filter => lambda{|results| results['rows].map{|row| row['value']}} # # example: - # view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw, :lib => {:module => "exports.name = 'module';" + # view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw" class RawViewSpec < BaseViewSpec def map_function options[:map] @@ -19,10 +19,6 @@ def map_function def reduce_function options[:reduce] end - - def lib - options[:lib] - end end end end diff --git a/lib/couch_potato/view/view_query.rb b/lib/couch_potato/view/view_query.rb index 473d86a5..c9b72e20 100644 --- a/lib/couch_potato/view/view_query.rb +++ b/lib/couch_potato/view/view_query.rb @@ -2,18 +2,13 @@ module CouchPotato module View # Used to query views (and create them if they don't exist). Usually you won't have to use this class directly. Instead it is used internally by the CouchPotato::Database.view method. class ViewQuery - def initialize(couchrest_database, design_document_name, view, list = nil, lib = nil, language = :javascript) + def initialize(couchrest_database, design_document_name, view, language = :javascript) @database = couchrest_database @design_document_name = design_document_name @view_name = view.keys[0] @map_function = view.values[0][:map] @reduce_function = view.values[0][:reduce] - @lib = lib @language = language - if list - @list_function = list.values[0] - @list_name = list.keys[0] - end end def query_view!(parameters = {}) @@ -42,34 +37,41 @@ def self.__updated_views def update_view design_doc = @database.get "_design/#{@design_document_name}" rescue nil original_views = design_doc && design_doc['views'].dup - original_lists = design_doc && design_doc['lists'] && design_doc['lists'].dup - view_updated unless design_doc.nil? + view_updated design_doc ||= empty_design_document - design_doc['views'][@view_name.to_s] = view_functions - if @lib - design_doc['views']['lib'] = (design_doc['views']['lib'] || {}).merge(@lib) + if CouchPotato::Config.single_design_document + design_doc['views'] = all_views + else + design_doc['views'][@view_name.to_s] = view_functions end - if @list_function - design_doc['lists'] ||= {} - design_doc['lists'][@list_name.to_s] = @list_function + if original_views != design_doc['views'] + @database.save_doc(design_doc) end - @database.save_doc(design_doc) if original_views != design_doc['views'] || original_lists != design_doc['lists'] end - def view_functions - if @reduce_function - {'map' => @map_function, 'reduce' => @reduce_function} - else - {'map' => @map_function} - end + def all_views + CouchPotato.views.flat_map do |klass| + specs = klass.views.map { |view_name, view| klass.execute_view(view_name, {}) } + specs.map do |klass_spec| + { klass_spec.view_name => view_functions(klass_spec.map_function, klass_spec.reduce_function) } + end + end.inject(&:merge) + end + + def view_functions(map_function = @map_function, reduce_function = @reduce_function) + {'map' => map_function, 'reduce' => reduce_function}.compact end def empty_design_document - {'views' => {}, 'lists' => {}, "_id" => "_design/#{@design_document_name}", "language" => @language.to_s} + {'views' => {}, "_id" => "_design/#{@design_document_name}", "language" => @language.to_s} end def view_has_been_updated? - updated_views[[@design_document_name, @view_name]] + if CouchPotato::Config.single_design_document + updated_views.any? + else + updated_views[[@design_document_name, @view_name]] + end end def view_updated @@ -81,11 +83,7 @@ def updated_views end def query_view(parameters) - if @list_name - @database.connection.get CouchRest.paramify_url("/#{@database.name}/_design/#{@design_document_name}/_list/#{@list_name}/#{@view_name}", parameters) - else - @database.view view_url, parameters - end + @database.view view_url, parameters end def view_url diff --git a/spec/property_spec.rb b/spec/property_spec.rb index fdef3280..42c90bfe 100644 --- a/spec/property_spec.rb +++ b/spec/property_spec.rb @@ -418,7 +418,7 @@ def it_should_persist value it "should include complex datatypes fully inspected" do comment.title = {'en' => 'Blog post'} - expect(comment.inspect).to include('title: {"en"=>"Blog post"}') + expect(comment.inspect).to match(/title: {"en"\s*=>\s*"Blog post"}/) comment.title = nil expect(comment.inspect).to include('title: nil') diff --git a/spec/single_design_document_spec.rb b/spec/single_design_document_spec.rb new file mode 100644 index 00000000..97a4e337 --- /dev/null +++ b/spec/single_design_document_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe 'single design document' do + let(:db) { CouchPotato.database } + let(:couchrest_db) { db.couchrest_database } + + class Thing1 + include CouchPotato::Persistence + + property :title + + view :all, key: :title + end + + class Thing2 + include CouchPotato::Persistence + + property :name + + view :all, key: :name + end + + class Thing3 < Thing1 # should work with inheritance + property :tag + + view :by_tag, key: :tag + end + + before(:each) do + recreate_db + CouchPotato::Config.single_design_document = true + CouchPotato.views.select! { |v| [Thing1, Thing2, Thing3].include?(v) } # clear classes from other specs + end + + after(:each) do + CouchPotato::Config.single_design_document = false + end + + it 'creates a single design document for all views' do + thing1 = Thing1.new title: 't1' + db.save! thing1 + thing2 = Thing2.new name: 'n2' + db.save! thing2 + + db.view(Thing1.all) # create all views when querying the first one + + expect(couchrest_db.get('_design/couch_potato')['views'].keys) + .to(eq(['thing1-all', 'thing2-all', 'thing3-by_tag'])) + end + + it 'returns the correct models' do + thing1 = Thing1.new title: 't1' + db.save! thing1 + thing2 = Thing2.new name: 'n2' + db.save! thing2 + thing3 = Thing3.new tag: 'tag1' + db.save! thing3 + + expect(db.view(Thing1.all('t1'))).to eq([thing1]) + expect(db.view(Thing2.all('n2'))).to eq([thing2]) + expect(db.view(Thing3.by_tag('tag1'))).to eq([thing3]) + end +end diff --git a/spec/unit/attributes_spec.rb b/spec/unit/attributes_spec.rb index 19d3c61d..c2e4a679 100644 --- a/spec/unit/attributes_spec.rb +++ b/spec/unit/attributes_spec.rb @@ -87,7 +87,7 @@ class SpecialPlant < Plant plant = Plant.new expect do plant.length - end.to raise_error(NoMethodError, /undefined method `length'/) + end.to raise_error(NoMethodError, /undefined method [`']length'/) end end diff --git a/spec/unit/base_view_spec_spec.rb b/spec/unit/base_view_spec_spec.rb index 078dc105..8579c018 100644 --- a/spec/unit/base_view_spec_spec.rb +++ b/spec/unit/base_view_spec_spec.rb @@ -33,8 +33,7 @@ :group_level => 1, :reduce => false, :include_docs => true, - :inclusive_end => true, - :list_params => {} + :inclusive_end => true } }.not_to raise_error end @@ -54,70 +53,72 @@ expect(spec.view_parameters).to eq({:key => '2'}) end - it 'merges the list params' do - spec = CouchPotato::View::BaseViewSpec.new Object, 'all', {}, key: '2', list_params: {:x => 'y'} - expect(spec.view_parameters).to eq({:key => '2', :x => 'y'}) - end - - it "generates the design document path by snake_casing the class name but keeping double colons" do - spec = CouchPotato::View::BaseViewSpec.new 'Foo::BarBaz', '', {}, '' - expect(spec.design_document).to eq('foo::bar_baz') - end - - it "generates the design document independent of the view name by default" do - CouchPotato::Config.split_design_documents_per_view = false - spec = CouchPotato::View::BaseViewSpec.new 'User', 'by_login_and_email', {}, '' - expect(spec.design_document).to eq('user') - end - - it "generates the design document per view if configured to" do - CouchPotato::Config.split_design_documents_per_view = true - spec = CouchPotato::View::BaseViewSpec.new 'User', 'by_login_and_email', {}, '' - expect(spec.design_document).to eq('user_view_by_login_and_email') - end - - it 'adds the view name digest to the design doc name' do - CouchPotato::Config.split_design_documents_per_view = true - spec = CouchPotato::View::RawViewSpec.new 'User', 'by_login_and_email', - {digest_view_name: true, map: 'function(doc) {}'}, '' - - expect(spec.design_document).to eq('user_view_by_login_and_email-375c815fcb4f977f330a2edfadc7f74d') - end - - it 'builds the name digest by hashing the map and reduce function if there is one' do - CouchPotato::Config.split_design_documents_per_view = true - spec = CouchPotato::View::RawViewSpec.new 'User', 'by_login_and_email', - {digest_view_name: true, map: 'function(doc) {}', reduce: 'function(key, values) {}'}, '' - - expect(spec.design_document).to eq('user_view_by_login_and_email-c9f83cec3dab954a8ca56330006f187e') - end - - - it "generates the design document independent of the list name by default" do - CouchPotato::Config.split_design_documents_per_view = false - spec = CouchPotato::View::BaseViewSpec.new double(lists: nil, :to_s => 'User'), '', {list: 'test_list'}, {} - expect(spec.design_document).to eq('user') - end + context 'when single design document is enabled' do + before(:each) do + CouchPotato::Config.single_design_document = true + end - it "generates the design document per view if configured to" do - CouchPotato::Config.split_design_documents_per_view = true - spec = CouchPotato::View::BaseViewSpec.new double(lists: nil, :to_s => 'User'), '', {list: :test_list}, {} - expect(spec.design_document).to eq('user_list_test_list') - end + after(:each) do + CouchPotato::Config.single_design_document = false + end + + it 'generates one design document for all views' do + spec = CouchPotato::View::BaseViewSpec.new 'User', 'by_login_and_email', {}, '' - it "extracts the list name from the options" do - spec = CouchPotato::View::BaseViewSpec.new double(lists: nil), 'all', {list: :test_list}, {} - expect(spec.list_name).to eq(:test_list) + expect(spec.design_document).to eq('couch_potato') + end end - it "extracts the list from the view parameters" do - spec = CouchPotato::View::BaseViewSpec.new double(lists: nil), 'all', {}, {list: :test_list} - expect(spec.list_name).to eq(:test_list) - end + context 'when single design document is disabled' do + before(:each) do + CouchPotato::Config.single_design_document = false + end + + context 'and split design documents per view is enabled' do + before(:each) do + CouchPotato::Config.split_design_documents_per_view = true + end + + after(:each) do + CouchPotato::Config.split_design_documents_per_view = false + end + + it "generates one design document per view" do + spec = CouchPotato::View::BaseViewSpec.new 'User', 'by_login_and_email', {}, '' + expect(spec.design_document).to eq('user_view_by_login_and_email') + end + + it 'adds the view name digest to the design doc name' do + spec = CouchPotato::View::RawViewSpec.new 'User', 'by_login_and_email', + {digest_view_name: true, map: 'function(doc) {}'}, '' + + expect(spec.design_document).to eq('user_view_by_login_and_email-375c815fcb4f977f330a2edfadc7f74d') + end + + it 'builds the name digest by hashing the map and reduce function if there is one' do + spec = CouchPotato::View::RawViewSpec.new 'User', 'by_login_and_email', + {digest_view_name: true, map: 'function(doc) {}', reduce: 'function(key, values) {}'}, '' + + expect(spec.design_document).to eq('user_view_by_login_and_email-c9f83cec3dab954a8ca56330006f187e') + end + end - it "prefers the list name from the view parameters over the one from the options" do - spec = CouchPotato::View::BaseViewSpec.new double(lists: nil), 'all', {list: 'my_list'}, list: :test_list - expect(spec.list_name).to eq(:test_list) + context 'and split design documents per view is disabled' do + before(:each) do + CouchPotato::Config.split_design_documents_per_view = false + end + + it "generates the design document path by snake_casing the class name but keeping double colons" do + spec = CouchPotato::View::BaseViewSpec.new 'Foo::BarBaz', '', {}, '' + expect(spec.design_document).to eq('foo::bar_baz') + end + + it "generates the design document independent of the view name" do + spec = CouchPotato::View::BaseViewSpec.new 'User', 'by_login_and_email', {}, '' + expect(spec.design_document).to eq('user') + end + end + end it 'returns the view name' do @@ -146,13 +147,6 @@ end end - it "returns the list function" do - klass = double 'class' - allow(klass).to receive(:lists).with('test_list').and_return('') - spec = CouchPotato::View::BaseViewSpec.new klass, 'all', {list: 'test_list'}, {} - expect(spec.list_function).to eq('') - end - it 'reads the language from the couch potato config by default' do CouchPotato::Config.default_language = :ruby spec = CouchPotato::View::BaseViewSpec.new Object, 'all', {}, {} diff --git a/spec/unit/couch_potato_spec.rb b/spec/unit/couch_potato_spec.rb index d702cced..56a8d8c1 100644 --- a/spec/unit/couch_potato_spec.rb +++ b/spec/unit/couch_potato_spec.rb @@ -8,6 +8,7 @@ CouchPotato::Config.database_name = nil CouchPotato::Config.split_design_documents_per_view = false CouchPotato::Config.digest_view_names = false + CouchPotato::Config.single_design_document = false CouchPotato::Config.default_language = :javascript CouchPotato::Config.database_host = 'http://127.0.0.1:5984' CouchPotato::Config.additional_databases = {} @@ -35,12 +36,14 @@ test2: 'test2_db' }, split_design_documents_per_view: true, + single_design_document: true, digest_view_names: true, default_language: 'erlang' ) expect(CouchPotato::Config.database_name).to eq('testdb') expect(CouchPotato::Config.split_design_documents_per_view).to eq(true) + expect(CouchPotato::Config.single_design_document).to eq(true) expect(CouchPotato::Config.digest_view_names).to eq(true) expect(CouchPotato::Config.default_language).to eq('erlang') expect(CouchPotato::Config.database_host).to eq('http://10.0.0.1:2000') diff --git a/spec/unit/database_spec.rb b/spec/unit/database_spec.rb index 9fdc8d29..d979aac7 100644 --- a/spec/unit/database_spec.rb +++ b/spec/unit/database_spec.rb @@ -415,29 +415,10 @@ def set_errors allow(CouchPotato::View::ViewQuery).to receive_messages(new: double('view query', query_view!: { 'rows' => [@result] })) end - it 'initializes a view query with map/reduce/list/lib funtions' do - allow(@spec).to receive_messages(design_document: 'design_doc', view_name: 'my_view', - map_function: '', reduce_function: '', - lib: { test: '' }, - list_name: 'my_list', list_function: '', language: 'javascript') - expect(CouchPotato::View::ViewQuery).to receive(:new).with( - @couchrest_db, - 'design_doc', - { 'my_view' => { - map: '', - reduce: '' - } }, - { 'my_list' => '' }, - { test: '' }, - 'javascript' - ) - @db.view(@spec) - end - it 'initializes a view query with map/reduce/list funtions' do + it 'initializes a view query with map/reduce functions' do allow(@spec).to receive_messages(design_document: 'design_doc', view_name: 'my_view', map_function: '', reduce_function: '', - lib: nil, list_name: 'my_list', list_function: '', language: 'javascript') expect(CouchPotato::View::ViewQuery).to receive(:new).with( @couchrest_db, @@ -446,44 +427,11 @@ def set_errors map: '', reduce: '' } }, - { 'my_list' => '' }, - nil, 'javascript' ) @db.view(@spec) end - it 'initializes a view query with only map/reduce/lib functions' do - allow(@spec).to receive_messages(design_document: 'design_doc', view_name: 'my_view', - map_function: '', reduce_function: '', - list_name: nil, list_function: nil, - lib: { test: '' }) - expect(CouchPotato::View::ViewQuery).to receive(:new).with( - @couchrest_db, - 'design_doc', - { 'my_view' => { - map: '', - reduce: '' - } }, nil, { test: '' }, anything - ) - @db.view(@spec) - end - - it 'initializes a view query with only map/reduce functions' do - allow(@spec).to receive_messages(design_document: 'design_doc', view_name: 'my_view', - map_function: '', reduce_function: '', - lib: nil, list_name: nil, list_function: nil) - expect(CouchPotato::View::ViewQuery).to receive(:new).with( - @couchrest_db, - 'design_doc', - { 'my_view' => { - map: '', - reduce: '' - } }, nil, nil, anything - ) - @db.view(@spec) - end - it 'sets itself on returned docs that have an accessor' do allow(@result).to receive(:respond_to?).and_return(false) allow(@result).to receive(:respond_to?).with(:database=).and_return(true) diff --git a/spec/unit/lists_spec.rb b/spec/unit/lists_spec.rb deleted file mode 100644 index acce637c..00000000 --- a/spec/unit/lists_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe CouchPotato::View::Lists, '.list' do - it 'should make the list function available via .lists' do - clazz = Class.new - clazz.send :include, CouchPotato::View::Lists - clazz.list 'my_list', '' - - expect(clazz.lists('my_list')).to eq('') - end - - it 'should make the list available to subclasses' do - clazz = Class.new - clazz.send :include, CouchPotato::View::Lists - clazz.list 'my_list', '' - sub_clazz = Class.new clazz - - expect(sub_clazz.lists('my_list')).to eq('') - end -end diff --git a/spec/unit/rspec_matchers_spec.rb b/spec/unit/rspec_matchers_spec.rb index 9082f873..5458fa9e 100644 --- a/spec/unit/rspec_matchers_spec.rb +++ b/spec/unit/rspec_matchers_spec.rb @@ -36,22 +36,6 @@ expect(spec).to map({}).to([nil, "2013-05-17T15:00:00.000Z"]) end - it "should work with commonJS modules that use 'exports'" do - spec = double( - :map_function => "function(doc) { var test = require('views/lib/test'); emit(null, test.test); }", - :lib => {:test => "exports.test = 'test';"} - ) - expect(spec).to map({}).to([nil, "test"]) - end - - it "should work with commonJS modules that use 'module.exports'" do - spec = double( - :map_function => "function(doc) { var test = require('views/lib/test'); emit(null, test.test); }", - :lib => {:test => "module.exports.test = 'test';"} - ) - expect(spec).to map({}).to([nil, "test"]) - end - describe "failing specs" do before(:each) do @view_spec = double(:map_function => "function(doc) {emit(doc.name, null)}") @@ -145,22 +129,6 @@ expect(spec).to map_reduce({}).to({"key" => nil, "value" => "2013-05-17T15:00:00.000Z"}) end - it "should handle CommonJS requires for modules that use 'exports'" do - spec = double( - :map_function => "function() { var test = require('views/lib/test'); emit(null, test.test); }", - :reduce_function => "function(keys, values) { return 'test' }", - :lib => {:test => "exports.test = 'test'"}) - expect(spec).to map_reduce({}).to({"key" => nil, "value" => "test"}) - end - - it "should handle CommonJS requires for modules that use 'module.exports'" do - spec = double( - :map_function => "function() { var test = require('views/lib/test'); emit(null, test.test); }", - :reduce_function => "function(keys, values) { return 'test' }", - :lib => {:test => "module.exports.test = 'test'"}) - expect(spec).to map_reduce({}).to({"key" => nil, "value" => "test"}) - end - it "should handle sum function" do spec = double( :map_function => "function(doc) { emit(null, doc.age); }", @@ -240,13 +208,13 @@ it "should have a nice error message for failing should" do expect { expect(@view_spec).to map_reduce(@docs).with_options(:group => false).to({"key" => nil, "value" => 9}) - }.to raise_error('Expected to map/reduce to [{"key"=>nil, "value"=>9}] but got [{"key"=>nil, "value"=>8}].') + }.to raise_error(%r{Expected to map/reduce to \[{"key"\s*=>\s*nil, "value"\s*=>\s*9}\] but got \[{"key"\s*=>\s*nil, "value"\s*=>\s*8}\].}) end it "should have a nice error message for failing should not" do expect { expect(@view_spec).not_to map_reduce(@docs).with_options(:group => false).to({"key" => nil, "value" => 8}) - }.to raise_error('Expected not to map/reduce to [{"key"=>nil, "value"=>8}] but did.') + }.to raise_error(%r{Expected not to map/reduce to \[{"key"\s*=>\s*nil, "value"\s*=>\s*8}\] but did.}) end end @@ -283,35 +251,3 @@ end end -describe CouchPotato::RSpec::ListAsMatcher do - before(:each) do - @view_spec = double(:list_function => "function() {var row = getRow(); send(JSON.stringify([{text: row.text + ' world'}]));}") - end - - it "should pass if the function return the expected json" do - expect(@view_spec).to list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello world'}]) - end - - it "should not pass if the function does not return the expected json" do - expect(@view_spec).not_to list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello there'}]) - end - - it "should work with date values" do - spec = double(:list_function => "function() { send(JSON.stringify([{date: new Date(1368802800000)}])); }") - expect(spec).to list({"rows" => [{}]}).as([{"date" => "2013-05-17T15:00:00.000Z"}]) - end - - describe "failing specs" do - it "should have a nice error message for failing should" do - expect { - expect(@view_spec).to list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello there'}]) - }.to raise_error('Expected to list as [{"text"=>"hello there"}] but got [{"text"=>"hello world"}].') - end - - it "should have a nice error message for failing should not" do - expect { - expect(@view_spec).not_to list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello world'}]) - }.to raise_error('Expected to not list as [{"text"=>"hello world"}] but did.') - end - end -end diff --git a/spec/unit/view_query_spec.rb b/spec/unit/view_query_spec.rb index f4cee60b..232616d3 100644 --- a/spec/unit/view_query_spec.rb +++ b/spec/unit/view_query_spec.rb @@ -16,14 +16,13 @@ it 'updates a view if it does not exist' do expect(db).to receive(:save_doc).with( { - 'views' => {'view' => {'map' => '', 'reduce' => ''}, 'lib' => {'test' => ''}}, - 'lists' => {}, + 'views' => {'view' => {'map' => '', 'reduce' => ''}}, "_id" => "_design/design", "language" => "javascript" } ) - CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '', :reduce => ''}}, nil, {'test' => ""}).query_view! + CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '', :reduce => ''}}).query_view! end it 'only updates a view once' do @@ -50,33 +49,19 @@ expect(db).to receive(:save_doc).with( { 'views' => {'view' => {'map' => '', 'reduce' => ''}}, - 'lists' => {}, "_id" => "_design/design", "language" => "erlang" + "_id" => "_design/design", "language" => "erlang" } ) CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '', :reduce => ''}}, - nil, nil, :erlang).query_view! + :erlang).query_view! end it "does not update a view when the views object haven't changed" do allow(db).to receive(:get).and_return({'views' => {'view' => {'map' => '', 'reduce' => ''}}}) expect(db).not_to receive(:save_doc) - CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '', :reduce => ''}}, nil, nil).query_view! - end - - it "does not update a view when the list function hasn't changed" do - allow(db).to receive(:get).and_return({'views' => {'view' => {'map' => '', 'reduce' => ''}}, 'lists' => {'list0' => ''}}) - expect(db).not_to receive(:save_doc) - CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '', :reduce => ''}}, :list0 => '').query_view! - end - - it "does not update a view when the lib function hasn't changed" do - allow(db).to receive(:get).and_return({'views' => {'view' => {'map' => '', 'reduce' => ''}, 'lib' => {'test' => ''}}}) - - expect(db).not_to receive(:save_doc) - - CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '', :reduce => ''}}, nil, {'test' => ""}).query_view! + CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '', :reduce => ''}}).query_view! end it 'updates a view when the map function has changed' do @@ -91,88 +76,9 @@ CouchPotato::View::ViewQuery.new(db, 'design', :view3 => {:map => ''}).query_view! end - it 'updates a view when the lib hash has changed' do - allow(db).to receive(:get).and_return({'views' => {'view4' => {'map' => ''}}}, 'lib' => {'test' => ""}) - - expect(db).to receive(:save_doc) - - CouchPotato::View::ViewQuery.new(db, 'design', {:view4 => {:map => ''}}, nil, {:test => ""}).query_view! - end - - it "doesn't override libs with different names" do - allow(db).to receive(:get).and_return({'views' => {'view5' => {'map' => ''}, 'lib' => {'test' => ""}}}) - expect(db).to receive(:save_doc).with({ - 'views' => { - 'view5' => {'map' => ''}, - 'lib' => {'test' => '', 'test1' => ''} - } - }) - CouchPotato::View::ViewQuery.new(db, 'design', {:view5 => {:map => ''}}, nil, {'test1' => ''}).query_view! - end - - it 'overrides libs with the same name' do - allow(db).to receive(:get).and_return({'views' => {'view6' => {'map' => ''}, 'lib' => {'test' => ""}}}) - - expect(db).to receive(:save_doc).with({ - 'views' => { - 'view6' => {'map' => ''}, - 'lib' => {'test' => ''} - }, - }) - - CouchPotato::View::ViewQuery.new(db, 'design', {:view6 => {:map => ''}}, nil, {'test' => ''}).query_view! - end - - it 'does not pass in reduce or lib keys if there is no lib or reduce object' do - allow(db).to receive(:get).and_return({'views' => {}}) - expect(db).to receive(:save_doc).with({'views' => {'view7' => {'map' => ''}}}) - CouchPotato::View::ViewQuery.new(db, 'design', :view7 => {:map => '', :reduce => nil}).query_view! - end - it 'updates a view when the reduce function has changed' do allow(db).to receive(:get).and_return({'views' => {'view8' => {'map' => '', 'reduce' => ''}}}) expect(db).to receive(:save_doc) CouchPotato::View::ViewQuery.new(db, 'design', :view8 => {:map => '', :reduce => ''}).query_view! end - - it 'updates a view when the list function has changed' do - allow(db).to receive(:get).and_return({ - 'views' => {'view9' => {'map' => '', 'reduce' => ''}}, - 'lists' => {'list1' => ''} - }) - expect(db).to receive(:save_doc) - CouchPotato::View::ViewQuery.new(db, 'design', {:view9 => {:map => '', :reduce => ''}}, :list1 => '').query_view! - end - - it "updates a view when there wasn't a list function but now there is one" do - allow(db).to receive(:get).and_return({ - 'views' => {'view10' => {'map' => '', 'reduce' => ''}} - }) - expect(db).to receive(:save_doc) - CouchPotato::View::ViewQuery.new(db, 'design', {:view10 => {:map => '', :reduce => ''}}, :list1 => '').query_view! - end - - it "does not update a view when there is a list function but no list function is passed" do - allow(db).to receive(:get).and_return({ - 'views' => {'view11' => {'map' => '', 'reduce' => ''}}, - 'lists' => {'list1' => ''} - }) - expect(db).not_to receive(:save_doc) - CouchPotato::View::ViewQuery.new(db, 'design', {:view11 => {:map => '', :reduce => ''}}, {}).query_view! - end - - it "does not update a view when there were no lists before and no list function is passed" do - allow(db).to receive(:get).and_return({ - 'views' => {'view12' => {'map' => '', 'reduce' => ''}} - }) - expect(db).not_to receive(:save_doc) - CouchPotato::View::ViewQuery.new(db, 'design', {:view12 => {:map => '', :reduce => ''}}, {}).query_view! - end - - it "queries the database directly when querying a list" do - allow(db).to receive(:name){'my_database'} - - expect(db.connection).to receive(:get).with('/my_database/_design/my_design/_list/list1/view13?key=1') - CouchPotato::View::ViewQuery.new(db, 'my_design', {:view13 => {:map => '', :reduce => ''}}, :list1 => '').query_view!(:key => 1) - end end diff --git a/spec/view_updates_spec.rb b/spec/view_updates_spec.rb index 9f9736fe..229e825b 100644 --- a/spec/view_updates_spec.rb +++ b/spec/view_updates_spec.rb @@ -7,8 +7,27 @@ end it "should update a view that doesn't match the given functions" do - CouchPotato::View::ViewQuery.new(@db, 'test_design1', {'test_view' => {:map => 'function(doc) {}', :reduce => 'function() {}'}}, nil).query_view! # create view - CouchPotato::View::ViewQuery.new(@db, 'test_design1', {'test_view' => {:map => 'function(doc) {emit(doc.id, null)}', :reduce => 'function(key, values) {return sum(values)}'}}, nil).query_view! + CouchPotato::View::ViewQuery.new( + @db, + 'test_design1', { + 'test_view' => { + :map => 'function(doc) { }', + :reduce => 'function(key, values) { return []; }' + } + } + ).query_view! # create view + CouchPotato::View::ViewQuery.clear_cache + CouchPotato::View::ViewQuery.new( + @db, + 'test_design1', + { + 'test_view' => { + :map => 'function(doc) {emit(doc.id, null)}', + :reduce => 'function(key, values) {return sum(values)}' + } + } + ).query_view! + expect(CouchPotato.database.load('_design/test_design1')['views']['test_view']).to eq({ 'map' => 'function(doc) {emit(doc.id, null)}', 'reduce' => 'function(key, values) {return sum(values)}' @@ -16,9 +35,38 @@ end it "should only update a view once to avoid writing the view for every request" do - CouchPotato::View::ViewQuery.new(@db, 'test_design2', {'test_view' => {:map => 'function(doc) {}', :reduce => 'function() {}'}}, nil).query_view! # create view - CouchPotato::View::ViewQuery.new(@db, 'test_design2', {'test_view' => {:map => 'function(doc) {emit(doc.id, null)}', :reduce => 'function(key, values) {return sum(values)}'}}, nil).query_view! - CouchPotato::View::ViewQuery.new(@db, 'test_design2', {'test_view' => {:map => 'function(doc) {}', :reduce => 'function() {}'}}, nil).query_view! + CouchPotato::View::ViewQuery.new( + @db, + 'test_design2', + { + 'test_view' => { + :map => 'function(doc) {}', + :reduce => 'function() {}' + } + } + ).query_view! # create view + CouchPotato::View::ViewQuery.clear_cache + CouchPotato::View::ViewQuery.new( + @db, + 'test_design2', + { + 'test_view' => { + :map => 'function(doc) {emit(doc.id, null)}', + :reduce => 'function(key, values) {return sum(values)}' + } + } + ).query_view! + CouchPotato::View::ViewQuery.new( + @db, + 'test_design2', + { + 'test_view' => { + :map => 'function(doc) {}', + :reduce => 'function() {}' + } + } + ).query_view! + expect(CouchPotato.database.load('_design/test_design2')['views']['test_view']).to eq({ 'map' => 'function(doc) {emit(doc.id, null)}', 'reduce' => 'function(key, values) {return sum(values)}' diff --git a/spec/views_spec.rb b/spec/views_spec.rb index 29e2fce8..f547d80d 100644 --- a/spec/views_spec.rb +++ b/spec/views_spec.rb @@ -225,7 +225,7 @@ class ErlangBuild describe 'with array as key' do it 'should create a map function with the composite key' do - expect(CouchPotato::View::ViewQuery).to receive(:new) do |_db, _design_name, view, _list| + expect(CouchPotato::View::ViewQuery).to receive(:new) do |_db, _design_name, view| expect(view['key_array_timeline'][:map]).to match(/emit\(\[doc\['time'\], doc\['state'\]\]/) double('view query', query_view!: { 'rows' => [] }) @@ -362,39 +362,6 @@ class ErlangBuild end end - describe 'list functions' do - class Coworker - include CouchPotato::Persistence - - property :name - - view :all_with_list, key: :name, list: :append_doe - view :all, key: :name - - list :append_doe, <<-JS - function(head, req) { - var row; - send('{"rows": ['); - while(row = getRow()) { - row.doc.name = row.doc.name + ' doe'; - send(JSON.stringify(row)); - }; - send(']}'); - } - JS - end - - it 'should use the list function declared at class level' do - @db.save! Coworker.new(name: 'joe') - expect(@db.view(Coworker.all_with_list).first.name).to eq('joe doe') - end - - it 'should use the list function passed at runtime' do - @db.save! Coworker.new(name: 'joe') - expect(@db.view(Coworker.all(list: :append_doe)).first.name).to eq('joe doe') - end - end - describe 'with stale views' do it 'does not return deleted documents' do build = Build.new