#-- # Copyright (C) 2006 Dimitrij Denissenko # Please read LICENSE document for more information. #++ module Acts module Previewable @@previewable_class_names = [] mattr_accessor :previewable_class_names def self.included(base) base.extend(ClassMethods) end def self.previewable_classes previewable_class_names.collect{|name| name.constantize} end def self.register(klass) self.previewable_class_names = (self.previewable_class_names + [klass.name]).uniq.sort end module ClassMethods def define_previewable(options = {}) unless previewable? cattr_accessor :previewable_options self.previewable_options = options unless Acts::Previewable.previewable_class_names.include?(self.name) Acts::Previewable.register(self) end include InstanceMethods end end def previewable? self.included_modules.include?(InstanceMethods) end end module InstanceMethods def self.included(base) base.extend(GeneralSingletonMethods) if base.previewable_options[:searchable_columns] base.extend(SearchSingletonMethods) end end def previewable @previewable ||= Acts::Previewable::InstanceBase.new(self) end end module GeneralSingletonMethods def previewable @previewable ||= Acts::Previewable::ClassBase.new(self) end end module SearchSingletonMethods def token_search(query) return [] if query.blank? || query.blank?.to_s.strip.blank? # Empty search yields 0 results tokens = query.split.collect {|c| "%#{c.downcase}%"}.uniq find_all_for_current_project_by_tokens(tokens) end def find_all_for_current_project_by_tokens(tokens) options = previewable.find_options || {} options[:limit] ||= 10 options[:order] ||= "#{table_name}.#{previewable.date_column} DESC" columns = previewable.searchable_columns sql = columns.collect{|f| "LOWER(#{f}) LIKE ?" }.join(' OR ') sql = (["(#{sql})"] * tokens.size).join(' AND ') options[:conditions] = [sql] tokens.each do |t| columns.size.times do options[:conditions] << t end end with_scope(:find => previewable.project_scope) do find(:all, options) end end end class InstanceBase attr_reader :base, :title, :content, :permalink_options def initialize(base) previewable_options = base.class.previewable_options option = previewable_options[:title] @title = if option.is_a?(Proc) option.call(base) else base.send(option) end option = previewable_options[:content] @content = if option.is_a?(Proc) [option.call(base), 0] elsif option.is_a?(Array) [base.send(option[0]), option[1]] else [base.send(option[0]), 0] end @base = base end def date base.send(base.class.previewable.date_column) end def permalink_options options = base.class.previewable.url url_params = base.class.previewable.url_params if url_params.is_a?(Proc) options.merge!(url_params.call(base)) end options end end class ClassBase attr_reader :date_column, :base, :url, :url_params def initialize(base) option = base.previewable_options[:project_scope] @project_scope = if option.is_a?(Proc) option elsif base.column_names.include?('project_id') Proc.new{ {:conditions => ["#{base.table_name}.project_id = ?", Project.current.id]} } else raise 'Previewable project scope could not be determined' end option = base.previewable_options[:date_column] @date_column = if option && base.column_names.include?(option.to_s) option elsif base.column_names.include?('updated_at') 'updated_at' elsif base.column_names.include?('created_at') 'created_at' else raise 'Previewable date could not be determined' end option = base.previewable_options[:url] params = option.delete(:params) @url = option if params.is_a?(Proc) @url_params = params end if @url[:controller].blank? raise "Previewable url [#{@url.inspect}] is invalid" end @url[:action] ||= 'index' @base = base end def searchable? base.respond_to?(:token_search) end def feedable? !self.feed.blank? end def feed base.previewable_options[:feed] end def find_options base.previewable_options[:find_options].dup rescue nil end def searchable_columns base.previewable_options[:searchable_columns] end def project_scope scope = @project_scope.call scope.delete(:order) scope end def viewable?(by_user = User.current) by_user.access_permitted?(url) end end end end ActiveRecord::Base.send(:include, Acts::Previewable) model_paths = Dependencies.load_paths.collect do |pt| pt.match(/models/) ? File.expand_path(pt + '/**/*.rb') : nil end.compact.each do |mp| Dir.glob(mp).reject do |f| !File.read(f).match(/define_previewable/) end.each do |f| File.basename(f, '.rb').camelize.constantize end end