mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 15:48:43 -05:00
speed up startup (avoid loading some gems on startup)
correct group permission leaks add Discourse.cache for richer caching support
This commit is contained in:
parent
9b33e826f2
commit
b6bf95e741
21 changed files with 250 additions and 66 deletions
29
Gemfile
29
Gemfile
|
@ -63,7 +63,7 @@ gem 'sinatra', require: nil
|
||||||
gem 'slim' # required for sidekiq-web
|
gem 'slim' # required for sidekiq-web
|
||||||
gem 'therubyracer', require: 'v8'
|
gem 'therubyracer', require: 'v8'
|
||||||
gem 'thin'
|
gem 'thin'
|
||||||
gem 'diffy'
|
gem 'diffy', require: false
|
||||||
|
|
||||||
# Gem that enables support for plugins. It is required.
|
# Gem that enables support for plugins. It is required.
|
||||||
gem 'discourse_plugin', path: 'vendor/gems/discourse_plugin'
|
gem 'discourse_plugin', path: 'vendor/gems/discourse_plugin'
|
||||||
|
@ -86,26 +86,27 @@ group :assets do
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'fakeweb', '~> 1.3.0'
|
gem 'fakeweb', '~> 1.3.0', require: false
|
||||||
gem 'minitest'
|
gem 'minitest', require: false
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test, :development do
|
group :test, :development do
|
||||||
gem 'jshint_on_rails'
|
gem 'jshint_on_rails'
|
||||||
gem 'guard-jshint-on-rails'
|
gem 'listen', require: false
|
||||||
gem 'certified'
|
gem 'guard-jshint-on-rails', require: false
|
||||||
gem 'fabrication'
|
gem 'certified', require: false
|
||||||
gem 'guard-jasmine'
|
gem 'fabrication', require: false
|
||||||
gem 'guard-rspec'
|
gem 'guard-jasmine', require: false
|
||||||
gem 'guard-spork'
|
gem 'guard-rspec', require: false
|
||||||
|
gem 'guard-spork', require: false
|
||||||
gem 'jasminerice'
|
gem 'jasminerice'
|
||||||
gem 'mocha', require: false
|
gem 'mocha', require: false
|
||||||
gem 'rb-fsevent'
|
gem 'rb-fsevent', require: false
|
||||||
gem 'rb-inotify', '~> 0.9', require: RUBY_PLATFORM.include?('linux') && 'rb-inotify'
|
gem 'rb-inotify', '~> 0.9', require: false
|
||||||
gem 'rspec-rails'
|
gem 'rspec-rails', require: false
|
||||||
gem 'shoulda'
|
gem 'shoulda', require: false
|
||||||
gem 'simplecov', require: false
|
gem 'simplecov', require: false
|
||||||
gem 'terminal-notifier-guard', require: RUBY_PLATFORM.include?('darwin') && 'terminal-notifier-guard'
|
gem 'terminal-notifier-guard', require: false
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
|
|
|
@ -482,6 +482,7 @@ DEPENDENCIES
|
||||||
jquery-rails
|
jquery-rails
|
||||||
jshint_on_rails
|
jshint_on_rails
|
||||||
librarian (>= 0.0.25)
|
librarian (>= 0.0.25)
|
||||||
|
listen
|
||||||
lru_redux
|
lru_redux
|
||||||
message_bus!
|
message_bus!
|
||||||
minitest
|
minitest
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
require 'rb-inotify' if RUBY_PLATFORM.include?('linux')
|
||||||
|
require 'terminal-notifier-guard' if RUBY_PLATFORM.include?('darwin')
|
||||||
|
|
||||||
phantom_path = File.expand_path('~/phantomjs/bin/phantomjs')
|
phantom_path = File.expand_path('~/phantomjs/bin/phantomjs')
|
||||||
phantom_path = nil unless File.exists?(phantom_path)
|
phantom_path = nil unless File.exists?(phantom_path)
|
||||||
|
|
||||||
|
@ -27,6 +30,7 @@ guard 'jshint-on-rails', config_path: 'config/jshint.yml' do
|
||||||
end
|
end
|
||||||
|
|
||||||
unless ENV["USING_AUTOSPEC"]
|
unless ENV["USING_AUTOSPEC"]
|
||||||
|
|
||||||
puts "Sam strongly recommends you Run: `bundle exec rake autospec` in favor of guard for specs, set USING_AUTOSPEC in .rvmrc to disable from Guard"
|
puts "Sam strongly recommends you Run: `bundle exec rake autospec` in favor of guard for specs, set USING_AUTOSPEC in .rvmrc to disable from Guard"
|
||||||
guard :spork, wait: 120 do
|
guard :spork, wait: 120 do
|
||||||
watch('config/application.rb')
|
watch('config/application.rb')
|
||||||
|
|
|
@ -99,7 +99,7 @@ class ApplicationController < ActionController::Base
|
||||||
guardian.current_user.sync_notification_channel_position
|
guardian.current_user.sync_notification_channel_position
|
||||||
end
|
end
|
||||||
|
|
||||||
store_preloaded("site", Site.cached_json)
|
store_preloaded("site", Site.cached_json(current_user))
|
||||||
|
|
||||||
if current_user.present?
|
if current_user.present?
|
||||||
store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, root: false)))
|
store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, root: false)))
|
||||||
|
|
|
@ -3,7 +3,7 @@ require_dependency 'search'
|
||||||
class SearchController < ApplicationController
|
class SearchController < ApplicationController
|
||||||
|
|
||||||
def query
|
def query
|
||||||
search_result = Search.query(params[:term], current_user, params[:type_filter], SiteSetting.min_search_term_length)
|
search_result = Search.query(params[:term], guardian, params[:type_filter], SiteSetting.min_search_term_length)
|
||||||
render_json_dump(search_result.as_json)
|
render_json_dump(search_result.as_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,16 @@ class Category < ActiveRecord::Base
|
||||||
|
|
||||||
scope :latest, ->{ order('topic_count desc') }
|
scope :latest, ->{ order('topic_count desc') }
|
||||||
|
|
||||||
|
scope :secured, lambda { |guardian|
|
||||||
|
ids = nil
|
||||||
|
ids = guardian.secure_category_ids if guardian
|
||||||
|
if ids.present?
|
||||||
|
where("categories.secure ='f' or categories.id = :cats ", cats: ids)
|
||||||
|
else
|
||||||
|
where("categories.secure ='f'")
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
delegate :post_template, to: 'self.class'
|
delegate :post_template, to: 'self.class'
|
||||||
|
|
||||||
# Internal: Update category stats: # of topics in past year, month, week for
|
# Internal: Update category stats: # of topics in past year, month, week for
|
||||||
|
|
|
@ -3,19 +3,14 @@ class CategoryList
|
||||||
|
|
||||||
attr_accessor :categories, :topic_users, :uncategorized
|
attr_accessor :categories, :topic_users, :uncategorized
|
||||||
|
|
||||||
def initialize(current_user)
|
def initialize(guardian)
|
||||||
@categories = Category
|
@categories = Category
|
||||||
.includes(featured_topics: [:category])
|
.includes(featured_topics: [:category])
|
||||||
.includes(:featured_users)
|
.includes(:featured_users)
|
||||||
.where('topics.visible' => true)
|
.where('topics.visible' => true)
|
||||||
|
.secured(guardian)
|
||||||
.order('categories.topics_week desc, categories.topics_month desc, categories.topics_year desc')
|
.order('categories.topics_week desc, categories.topics_month desc, categories.topics_year desc')
|
||||||
|
|
||||||
allowed_ids = current_user ? current_user.secure_category_ids : nil
|
|
||||||
if allowed_ids.present?
|
|
||||||
@categories = @categories.where("categories.secure = 'f' OR categories.id in (?)", allowed_ids)
|
|
||||||
else
|
|
||||||
@categories = @categories.where("categories.secure = 'f'")
|
|
||||||
end
|
|
||||||
|
|
||||||
@categories = @categories.to_a
|
@categories = @categories.to_a
|
||||||
|
|
||||||
|
@ -56,7 +51,7 @@ class CategoryList
|
||||||
@categories.insert(insert_at || @categories.size, uncategorized)
|
@categories.insert(insert_at || @categories.size, uncategorized)
|
||||||
end
|
end
|
||||||
|
|
||||||
unless Guardian.new(current_user).can_create?(Category)
|
unless guardian.can_create?(Category)
|
||||||
# Remove categories with no featured topics unless we have the ability to edit one
|
# Remove categories with no featured topics unless we have the ability to edit one
|
||||||
@categories.delete_if { |c| c.featured_topics.blank? }
|
@categories.delete_if { |c| c.featured_topics.blank? }
|
||||||
else
|
else
|
||||||
|
@ -69,7 +64,7 @@ class CategoryList
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get forum topic user records if appropriate
|
# Get forum topic user records if appropriate
|
||||||
if current_user.present?
|
if guardian.current_user
|
||||||
topics = []
|
topics = []
|
||||||
@categories.each { |c| topics << c.featured_topics }
|
@categories.each { |c| topics << c.featured_topics }
|
||||||
topics << @uncategorized
|
topics << @uncategorized
|
||||||
|
@ -77,7 +72,7 @@ class CategoryList
|
||||||
topics.flatten! if topics.present?
|
topics.flatten! if topics.present?
|
||||||
topics.compact! if topics.present?
|
topics.compact! if topics.present?
|
||||||
|
|
||||||
topic_lookup = TopicUser.lookup_for(current_user, topics)
|
topic_lookup = TopicUser.lookup_for(guardian.current_user, topics)
|
||||||
|
|
||||||
# Attach some data for serialization to each topic
|
# Attach some data for serialization to each topic
|
||||||
topics.each { |ft| ft.user_data = topic_lookup[ft.id] }
|
topics.each { |ft| ft.user_data = topic_lookup[ft.id] }
|
||||||
|
|
|
@ -5,6 +5,10 @@ require_dependency 'trust_level'
|
||||||
class Site
|
class Site
|
||||||
include ActiveModel::Serialization
|
include ActiveModel::Serialization
|
||||||
|
|
||||||
|
def initialize(guardian)
|
||||||
|
@guardian = guardian
|
||||||
|
end
|
||||||
|
|
||||||
def site_setting
|
def site_setting
|
||||||
SiteSetting
|
SiteSetting
|
||||||
end
|
end
|
||||||
|
@ -22,27 +26,32 @@ class Site
|
||||||
end
|
end
|
||||||
|
|
||||||
def categories
|
def categories
|
||||||
Category.latest.includes(:topic_only_relative_url)
|
Category
|
||||||
|
.secured(@guardian)
|
||||||
|
.latest
|
||||||
|
.includes(:topic_only_relative_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def archetypes
|
def archetypes
|
||||||
Archetype.list.reject { |t| t.id == Archetype.private_message }
|
Archetype.list.reject { |t| t.id == Archetype.private_message }
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.cache_key
|
def cache_key
|
||||||
"site_json"
|
k ="site_json_cats_"
|
||||||
|
k << @guardian.secure_category_ids.join("_") if @guardian
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.cached_json
|
def self.cached_json(guardian)
|
||||||
# Sam: bumping this way down, SiteSerializer will serialize post actions as well,
|
# Sam: bumping this way down, SiteSerializer will serialize post actions as well,
|
||||||
# On my local this was not being flushed as post actions types changed, it turn this
|
# On my local this was not being flushed as post actions types changed, it turn this
|
||||||
# broke local.
|
# broke local.
|
||||||
Rails.cache.fetch(Site.cache_key, expires_in: 1.minute) do
|
site = Site.new(guardian)
|
||||||
MultiJson.dump(SiteSerializer.new(Site.new, root: false))
|
Discourse.cache.fetch(site.cache_key, family: "site", expires_in: 1.minute) do
|
||||||
|
MultiJson.dump(SiteSerializer.new(site, root: false))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.invalidate_cache
|
def self.invalidate_cache
|
||||||
Rails.cache.delete(Site.cache_key)
|
Discourse.cache.delete_by_family("site")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -565,9 +565,10 @@ class User < ActiveRecord::Base
|
||||||
|
|
||||||
def secure_category_ids
|
def secure_category_ids
|
||||||
cats = self.staff? ? Category.select(:id).where(secure: true) : secure_categories.select('categories.id')
|
cats = self.staff? ? Category.select(:id).where(secure: true) : secure_categories.select('categories.id')
|
||||||
cats.map{|c| c.id}
|
cats.map{|c| c.id}.sort
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def cook
|
def cook
|
||||||
|
|
|
@ -4,7 +4,7 @@ class SiteSerializer < ApplicationSerializer
|
||||||
:notification_types,
|
:notification_types,
|
||||||
:post_types
|
:post_types
|
||||||
|
|
||||||
has_many :categories, embed: :objects
|
has_many :categories, serializer: BasicCategorySerializer, embed: :objects
|
||||||
has_many :post_action_types, embed: :objects
|
has_many :post_action_types, embed: :objects
|
||||||
has_many :trust_levels, embed: :objects
|
has_many :trust_levels, embed: :objects
|
||||||
has_many :archetypes, embed: :objects, serializer: ArchetypeSerializer
|
has_many :archetypes, embed: :objects, serializer: ArchetypeSerializer
|
||||||
|
|
55
lib/cache.rb
Normal file
55
lib/cache.rb
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# Standard Rails.cache is lacking support for this interface, possibly yank all in from redis:cache and start using this instead
|
||||||
|
#
|
||||||
|
|
||||||
|
class Cache
|
||||||
|
def initialize(redis=nil)
|
||||||
|
@redis = redis
|
||||||
|
end
|
||||||
|
|
||||||
|
def redis
|
||||||
|
@redis || $redis
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch(key, options={})
|
||||||
|
result = redis.get key
|
||||||
|
if result.nil?
|
||||||
|
if expiry = options[:expires_in]
|
||||||
|
if block_given?
|
||||||
|
result = yield
|
||||||
|
redis.setex(key, expiry, result)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if block_given?
|
||||||
|
result = yield
|
||||||
|
redis.set(key, result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if family = family_key(options[:family])
|
||||||
|
redis.sadd(family, key)
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(key)
|
||||||
|
redis.del(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_by_family(key)
|
||||||
|
k = family_key(key)
|
||||||
|
redis.smembers(k).each do |member|
|
||||||
|
delete(member)
|
||||||
|
end
|
||||||
|
redis.del(k)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def family_key(name)
|
||||||
|
if name
|
||||||
|
"FAMILY_#{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,3 +1,4 @@
|
||||||
|
require 'diffy'
|
||||||
# This class is used to generate diffs, it will be consumed by the UI on
|
# This class is used to generate diffs, it will be consumed by the UI on
|
||||||
# on the client the displays diffs.
|
# on the client the displays diffs.
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
require 'cache'
|
||||||
|
|
||||||
module Discourse
|
module Discourse
|
||||||
|
|
||||||
# When they try to do something they should be logged in for
|
# When they try to do something they should be logged in for
|
||||||
|
@ -12,6 +14,9 @@ module Discourse
|
||||||
# When something they want is not found
|
# When something they want is not found
|
||||||
class NotFound < Exception; end
|
class NotFound < Exception; end
|
||||||
|
|
||||||
|
def self.cache
|
||||||
|
@cache ||= Cache.new
|
||||||
|
end
|
||||||
|
|
||||||
# Get the current base URL for the current site
|
# Get the current base URL for the current site
|
||||||
def self.current_hostname
|
def self.current_hostname
|
||||||
|
|
|
@ -364,7 +364,7 @@ class Guardian
|
||||||
return true unless category.secure
|
return true unless category.secure
|
||||||
return false unless @user
|
return false unless @user
|
||||||
|
|
||||||
@user.secure_category_ids.include?(category.id)
|
secure_category_ids.include?(category.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_vote?(post, opts={})
|
def can_vote?(post, opts={})
|
||||||
|
@ -405,6 +405,6 @@ class Guardian
|
||||||
end
|
end
|
||||||
|
|
||||||
def secure_category_ids
|
def secure_category_ids
|
||||||
@user ? @user.secure_category_ids : []
|
@secure_category_ids ||= @user ? @user.secure_category_ids : []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,7 +24,7 @@ module Search
|
||||||
"
|
"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.post_query(current_user, type, args)
|
def self.post_query(guardian, type, args)
|
||||||
builder = SqlBuilder.new <<SQL
|
builder = SqlBuilder.new <<SQL
|
||||||
/*select*/
|
/*select*/
|
||||||
FROM topics AS ft
|
FROM topics AS ft
|
||||||
|
@ -64,20 +64,25 @@ s.search_data @@ TO_TSQUERY(:locale, :query)
|
||||||
AND ft.archetype <> '#{Archetype.private_message}'
|
AND ft.archetype <> '#{Archetype.private_message}'
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
|
add_allowed_categories(builder, guardian)
|
||||||
|
|
||||||
|
builder.exec(args)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.add_allowed_categories(builder, guardian)
|
||||||
allowed_categories = nil
|
allowed_categories = nil
|
||||||
allowed_categories = current_user.secure_category_ids if current_user
|
allowed_categories = guardian.secure_category_ids
|
||||||
if allowed_categories.present?
|
if allowed_categories.present?
|
||||||
builder.where("(c.id IS NULL OR c.secure = 'f' OR c.id in (:category_ids))", category_ids: allowed_categories)
|
builder.where("(c.id IS NULL OR c.secure = 'f' OR c.id in (:category_ids))", category_ids: allowed_categories)
|
||||||
else
|
else
|
||||||
builder.where("(c.id IS NULL OR c.secure = 'f')")
|
builder.where("(c.id IS NULL OR c.secure = 'f')")
|
||||||
end
|
end
|
||||||
|
|
||||||
builder.exec(args)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def self.category_query_sql
|
def self.category_query(guardian, args)
|
||||||
"SELECT 'category' AS type,
|
builder = SqlBuilder.new <<SQL
|
||||||
|
SELECT 'category' AS type,
|
||||||
c.name AS id,
|
c.name AS id,
|
||||||
'/category/' || c.slug AS url,
|
'/category/' || c.slug AS url,
|
||||||
c.name AS title,
|
c.name AS title,
|
||||||
|
@ -86,10 +91,16 @@ SQL
|
||||||
c.text_color
|
c.text_color
|
||||||
FROM categories AS c
|
FROM categories AS c
|
||||||
JOIN categories_search s on s.id = c.id
|
JOIN categories_search s on s.id = c.id
|
||||||
WHERE s.search_data @@ TO_TSQUERY(:locale, :query)
|
/*where*/
|
||||||
ORDER BY topics_month desc
|
ORDER BY topics_month desc
|
||||||
LIMIT :limit
|
LIMIT :limit
|
||||||
"
|
SQL
|
||||||
|
|
||||||
|
builder.where "s.search_data @@ TO_TSQUERY(:locale, :query)"
|
||||||
|
add_allowed_categories(builder,guardian)
|
||||||
|
|
||||||
|
builder.exec(args)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.current_locale_long
|
def self.current_locale_long
|
||||||
|
@ -108,7 +119,9 @@ SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
# needs current user for secure categories
|
# needs current user for secure categories
|
||||||
def self.query(term, current_user, type_filter=nil, min_search_term_length=3)
|
def self.query(term, guardian, type_filter=nil, min_search_term_length=3)
|
||||||
|
|
||||||
|
guardian ||= Guardian.new(nil)
|
||||||
|
|
||||||
return nil if term.blank?
|
return nil if term.blank?
|
||||||
|
|
||||||
|
@ -125,15 +138,17 @@ SQL
|
||||||
|
|
||||||
if type_filter.present?
|
if type_filter.present?
|
||||||
raise Discourse::InvalidAccess.new("invalid type filter") unless Search.facets.include?(type_filter)
|
raise Discourse::InvalidAccess.new("invalid type filter") unless Search.facets.include?(type_filter)
|
||||||
sql = Search.send("#{type_filter}_query_sql")
|
|
||||||
|
|
||||||
a = args.merge(limit: Search.per_facet * Search.facets.size)
|
a = args.merge(limit: Search.per_facet * Search.facets.size)
|
||||||
|
|
||||||
if type_filter.to_s == "post"
|
if type_filter.to_s == "post"
|
||||||
db_result = post_query(current_user, :post, a)
|
db_result = post_query(guardian, :post, a)
|
||||||
elsif type_filter.to_s == "topic"
|
elsif type_filter.to_s == "topic"
|
||||||
db_result = post_query(current_user, :topic, a)
|
db_result = post_query(guardian, :topic, a)
|
||||||
|
elsif type_filter.to_s == "category"
|
||||||
|
db_result = category_query(guardian, a)
|
||||||
else
|
else
|
||||||
|
sql = Search.send("#{type_filter}_query_sql")
|
||||||
db_result = ActiveRecord::Base.exec_sql(sql , a)
|
db_result = ActiveRecord::Base.exec_sql(sql , a)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -141,11 +156,10 @@ SQL
|
||||||
|
|
||||||
db_result = []
|
db_result = []
|
||||||
|
|
||||||
[user_query_sql, category_query_sql].each do |sql|
|
a = args.merge(limit: (Search.per_facet + 1))
|
||||||
db_result += ActiveRecord::Base.exec_sql(sql , args.merge(limit: (Search.per_facet + 1))).to_a
|
db_result += ActiveRecord::Base.exec_sql(user_query_sql, a).to_a
|
||||||
end
|
db_result += category_query(guardian, a).to_a
|
||||||
|
db_result += post_query(guardian, :topic, a).to_a
|
||||||
db_result += post_query(current_user, :topic, args.merge(limit: (Search.per_facet + 1))).to_a
|
|
||||||
end
|
end
|
||||||
|
|
||||||
db_result = db_result.to_a
|
db_result = db_result.to_a
|
||||||
|
@ -161,7 +175,7 @@ SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
if expected_topics > 0
|
if expected_topics > 0
|
||||||
tmp = post_query(current_user, :post, args.merge(limit: expected_topics * 3))
|
tmp = post_query(guardian, :post, args.merge(limit: expected_topics * 3))
|
||||||
|
|
||||||
topic_ids = Set.new db_result.map{|r| r["id"]}
|
topic_ids = Set.new db_result.map{|r| r["id"]}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,13 @@
|
||||||
|
|
||||||
desc "Run all specs automatically as needed"
|
desc "Run all specs automatically as needed"
|
||||||
task "autospec" => :environment do
|
task "autospec" => :environment do
|
||||||
|
|
||||||
|
if RUBY_PLATFORM.include?('linux')
|
||||||
|
require 'rb-inotify'
|
||||||
|
end
|
||||||
|
|
||||||
|
require 'listen'
|
||||||
|
|
||||||
puts "If file watching is not working you can force polling with: bundle exec rake autospec p l=3"
|
puts "If file watching is not working you can force polling with: bundle exec rake autospec p l=3"
|
||||||
require 'autospec/runner'
|
require 'autospec/runner'
|
||||||
|
|
||||||
|
|
63
spec/components/cache_spec.rb
Normal file
63
spec/components/cache_spec.rb
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'cache'
|
||||||
|
|
||||||
|
describe Cache do
|
||||||
|
|
||||||
|
let :cache do
|
||||||
|
Cache.new
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can delete by family" do
|
||||||
|
cache.fetch("key2", family: "my_family") do
|
||||||
|
"test"
|
||||||
|
end
|
||||||
|
|
||||||
|
cache.fetch("key", expires_in: 1.minute, family: "my_family") do
|
||||||
|
"test"
|
||||||
|
end
|
||||||
|
|
||||||
|
cache.delete_by_family("my_family")
|
||||||
|
cache.fetch("key").should be_nil
|
||||||
|
cache.fetch("key2").should be_nil
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can delete correctly" do
|
||||||
|
r = cache.fetch("key", expires_in: 1.minute) do
|
||||||
|
"test"
|
||||||
|
end
|
||||||
|
|
||||||
|
cache.delete("key")
|
||||||
|
cache.fetch("key").should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can store with expiry correctly" do
|
||||||
|
$redis.expects(:get).with("key").returns nil
|
||||||
|
$redis.expects(:setex).with("key", 60 , "bob")
|
||||||
|
|
||||||
|
r = cache.fetch("key", expires_in: 1.minute) do
|
||||||
|
"bob"
|
||||||
|
end
|
||||||
|
r.should == "bob"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can store and fetch correctly" do
|
||||||
|
$redis.expects(:get).with("key").returns nil
|
||||||
|
$redis.expects(:set).with("key", "bob")
|
||||||
|
|
||||||
|
r = cache.fetch "key" do
|
||||||
|
"bob"
|
||||||
|
end
|
||||||
|
r.should == "bob"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can fetch existing correctly" do
|
||||||
|
|
||||||
|
$redis.expects(:get).with("key").returns "bill"
|
||||||
|
|
||||||
|
r = cache.fetch "key" do
|
||||||
|
"bob"
|
||||||
|
end
|
||||||
|
r.should == "bill"
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,7 +4,7 @@ require 'category_list'
|
||||||
describe CategoryList do
|
describe CategoryList do
|
||||||
|
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
let(:category_list) { CategoryList.new(user) }
|
let(:category_list) { CategoryList.new(Guardian.new user) }
|
||||||
|
|
||||||
context "with no categories" do
|
context "with no categories" do
|
||||||
|
|
||||||
|
@ -39,9 +39,9 @@ describe CategoryList do
|
||||||
cat.allow(Group[:admins])
|
cat.allow(Group[:admins])
|
||||||
cat.save
|
cat.save
|
||||||
|
|
||||||
CategoryList.new(admin).categories.count.should == 1
|
CategoryList.new(Guardian.new admin).categories.count.should == 1
|
||||||
CategoryList.new(user).categories.count.should == 0
|
CategoryList.new(Guardian.new user).categories.count.should == 0
|
||||||
CategoryList.new(nil).categories.count.should == 0
|
CategoryList.new(Guardian.new nil).categories.count.should == 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -164,12 +164,20 @@ describe Search do
|
||||||
context 'categories' do
|
context 'categories' do
|
||||||
|
|
||||||
let!(:category) { Fabricate(:category) }
|
let!(:category) { Fabricate(:category) }
|
||||||
let(:result) { first_of_type(Search.query('amazing', nil), 'category') }
|
def result
|
||||||
|
first_of_type(Search.query('amazing', nil), 'category')
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns the correct result' do
|
it 'returns the correct result' do
|
||||||
result.should be_present
|
r = result
|
||||||
result['title'].should == category.name
|
r.should be_present
|
||||||
result['url'].should == "/category/#{category.slug}"
|
r['title'].should == category.name
|
||||||
|
r['url'].should == "/category/#{category.slug}"
|
||||||
|
|
||||||
|
category.deny(:all)
|
||||||
|
category.save
|
||||||
|
|
||||||
|
result.should_not be_present
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,12 +3,16 @@ require 'spec_helper'
|
||||||
describe SearchController do
|
describe SearchController do
|
||||||
|
|
||||||
it 'performs the query' do
|
it 'performs the query' do
|
||||||
Search.expects(:query).with('test', nil, nil, 3)
|
m = Guardian.new(nil)
|
||||||
|
Guardian.stubs(:new).returns(m)
|
||||||
|
Search.expects(:query).with('test', m, nil, 3)
|
||||||
xhr :get, :query, term: 'test'
|
xhr :get, :query, term: 'test'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'performs the query with a filter' do
|
it 'performs the query with a filter' do
|
||||||
Search.expects(:query).with('test', nil, 'topic', 3)
|
m = Guardian.new(nil)
|
||||||
|
Guardian.stubs(:new).returns(m)
|
||||||
|
Search.expects(:query).with('test', m, 'topic', 3)
|
||||||
xhr :get, :query, term: 'test', type_filter: 'topic'
|
xhr :get, :query, term: 'test', type_filter: 'topic'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,16 @@ Spork.prefork do
|
||||||
# Loading more in this block will cause your tests to run faster. However,
|
# Loading more in this block will cause your tests to run faster. However,
|
||||||
# if you change any configuration or code from libraries loaded here, you'll
|
# if you change any configuration or code from libraries loaded here, you'll
|
||||||
# need to restart spork for it take effect.
|
# need to restart spork for it take effect.
|
||||||
|
require 'fabrication'
|
||||||
|
require 'mocha/api'
|
||||||
|
require 'fakeweb'
|
||||||
|
require 'certified'
|
||||||
|
|
||||||
ENV["RAILS_ENV"] ||= 'test'
|
ENV["RAILS_ENV"] ||= 'test'
|
||||||
require File.expand_path("../../config/environment", __FILE__)
|
require File.expand_path("../../config/environment", __FILE__)
|
||||||
require 'rspec/rails'
|
require 'rspec/rails'
|
||||||
require 'rspec/autorun'
|
require 'rspec/autorun'
|
||||||
|
require 'shoulda'
|
||||||
|
|
||||||
# Requires supporting ruby files with custom matchers and macros, etc,
|
# Requires supporting ruby files with custom matchers and macros, etc,
|
||||||
# in spec/support/ and its subdirectories.
|
# in spec/support/ and its subdirectories.
|
||||||
|
|
Loading…
Reference in a new issue