mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-27 09:36:19 -05:00
added 2 new flag types: notify user and notify moderators
fixed up messed up user navigation refactored
This commit is contained in:
parent
0f362c5474
commit
e969eb14e8
20 changed files with 201 additions and 74 deletions
|
@ -9,7 +9,7 @@
|
|||
Discourse.ActionSummary = Discourse.Model.extend({
|
||||
|
||||
// Description for the action
|
||||
description: (function() {
|
||||
description: function() {
|
||||
var action = this.get('actionType.name_key');
|
||||
if (this.get('acted')) {
|
||||
if (this.get('count') <= 1) {
|
||||
|
@ -20,12 +20,12 @@ Discourse.ActionSummary = Discourse.Model.extend({
|
|||
} else {
|
||||
return Em.String.i18n('post.actions.by_others.' + action, { count: this.get('count') });
|
||||
}
|
||||
}).property('count', 'acted', 'actionType'),
|
||||
}.property('count', 'acted', 'actionType'),
|
||||
|
||||
canAlsoAction: (function() {
|
||||
canAlsoAction: function() {
|
||||
if (this.get('hidden')) return false;
|
||||
return this.get('can_act');
|
||||
}).property('can_act', 'hidden'),
|
||||
}.property('can_act', 'hidden'),
|
||||
|
||||
// Remove it
|
||||
removeAction: function() {
|
||||
|
@ -37,12 +37,13 @@ Discourse.ActionSummary = Discourse.Model.extend({
|
|||
|
||||
// Perform this action
|
||||
act: function(opts) {
|
||||
var action = this.get('actionType.name_key');
|
||||
|
||||
// Mark it as acted
|
||||
this.set('acted', true);
|
||||
this.set('count', this.get('count') + 1);
|
||||
this.set('can_act', false);
|
||||
this.set('can_undo', true);
|
||||
this.set('can_undo', action != 'notify_moderators' && action != 'notify_user');
|
||||
|
||||
// Add ourselves to the users who liked it if present
|
||||
if (this.present('users')) {
|
||||
|
@ -108,5 +109,4 @@ Discourse.ActionSummary = Discourse.Model.extend({
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -112,9 +112,10 @@ Discourse.Post = Discourse.Model.extend({
|
|||
|
||||
flagsAvailable: (function() {
|
||||
var _this = this;
|
||||
return Discourse.get('site.flagTypes').filter(function(item) {
|
||||
flags = Discourse.get('site.flagTypes').filter(function(item) {
|
||||
return _this.get("actionByName." + (item.get('name_key')) + ".can_act");
|
||||
});
|
||||
return flags;
|
||||
}).property('Discourse.site.flagTypes', 'actions_summary.@each.can_act'),
|
||||
|
||||
actionsHistory: (function() {
|
||||
|
|
|
@ -7,5 +7,30 @@
|
|||
@module Discourse
|
||||
**/
|
||||
Discourse.PostActionType = Discourse.Model.extend({
|
||||
|
||||
});
|
||||
|
||||
Discourse.BoundPostActionType = Discourse.PostActionType.extend({
|
||||
customPlaceholder: function(){
|
||||
return Em.String.i18n("flagging.custom_placeholder_" + this.get('name_key'));
|
||||
}.property('name_key'),
|
||||
|
||||
formattedName: function(){
|
||||
return this.get('name').replace("{{username}}", this.get('post.username'));
|
||||
}.property('name'),
|
||||
|
||||
messageChanged: function() {
|
||||
var len, message, minLen, _ref;
|
||||
minLen = 10;
|
||||
len = ((_ref = this.get('message')) ? _ref.length : void 0) || 0;
|
||||
this.set("customMessageLengthClasses", "too-short custom-message-length");
|
||||
if (len === 0) {
|
||||
message = Em.String.i18n("flagging.custom_message.at_least", { n: minLen });
|
||||
} else if (len < minLen) {
|
||||
message = Em.String.i18n("flagging.custom_message.more", { n: minLen - len });
|
||||
} else {
|
||||
message = Em.String.i18n("flagging.custom_message.left", { n: 500 - len });
|
||||
this.set("customMessageLengthClasses", "ok custom-message-length");
|
||||
}
|
||||
this.set("customMessageLength", message);
|
||||
}.observes("message")
|
||||
});
|
||||
|
|
|
@ -17,14 +17,14 @@ Discourse.Site = Discourse.Model.extend({
|
|||
return result;
|
||||
}).property('notification_types'),
|
||||
|
||||
flagTypes: (function() {
|
||||
flagTypes: function() {
|
||||
var postActionTypes;
|
||||
postActionTypes = this.get('post_action_types');
|
||||
if (!postActionTypes) {
|
||||
return [];
|
||||
}
|
||||
return postActionTypes.filterProperty('is_flag', true);
|
||||
}).property('post_action_types.@each'),
|
||||
}.property('post_action_types.@each'),
|
||||
|
||||
postActionTypeById: function(id) {
|
||||
return this.get("postActionByIdLookup.action" + id);
|
||||
|
@ -49,7 +49,7 @@ Discourse.Site.reopenClass({
|
|||
result.postActionByIdLookup = Em.Object.create();
|
||||
result.post_action_types = result.post_action_types.map(function(p) {
|
||||
var actionType;
|
||||
actionType = Discourse.Model.create(p);
|
||||
actionType = Discourse.PostActionType.create(p);
|
||||
result.postActionByIdLookup.set("action" + p.id, actionType);
|
||||
return actionType;
|
||||
});
|
||||
|
|
|
@ -14,8 +14,12 @@ Discourse.UserActivityRoute = Discourse.Route.extend({
|
|||
|
||||
setupController: function(controller) {
|
||||
var userController = this.controllerFor('user');
|
||||
userController.set('filter', null);
|
||||
controller.set('content', userController.get('content'));
|
||||
var user = userController.get('content');
|
||||
controller.set('content', user);
|
||||
user.set('filter', null);
|
||||
if (user.get('streamFilter')) {
|
||||
user.filterStream(null);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<div class="modal-body">
|
||||
<div class="modal-body flag-modal">
|
||||
{{#if view.post.flagsAvailable}}
|
||||
<form>
|
||||
{{#each view.post.flagsAvailable}}
|
||||
{{#each view.boundFlags}}
|
||||
<div class='controls'>
|
||||
<label class='radio'>
|
||||
<input type='radio' id="radio_{{unbound name_key}}" {{action changePostActionType this target="view"}} name='post_action_type_index'> <strong>{{name}}</strong>
|
||||
<input type='radio' id="radio_{{unbound name_key}}" {{action changePostActionType this target="view"}} name='post_action_type_index'> <strong>{{formattedName}}</strong>
|
||||
{{#if is_custom_flag}}
|
||||
{{#unless view.isCustomFlag}}
|
||||
{{#unless selected}}
|
||||
<div class='description'>{{{description}}}</div>
|
||||
{{/unless}}
|
||||
{{else}}
|
||||
|
@ -16,9 +16,9 @@
|
|||
{{/if}}
|
||||
</label>
|
||||
{{#if is_custom_flag}}
|
||||
{{#if view.isCustomFlag}}
|
||||
{{view Ember.TextArea name="message" class="flag-message" placeholderBinding="view.customPlaceholder" valueBinding="view.customFlagMessage"}}
|
||||
<div {{bindAttr class="view.customMessageLengthClasses"}}>{{view.customMessageLength}}</div>
|
||||
{{#if selected}}
|
||||
{{view Ember.TextArea name="message" class="flag-message" placeholderBinding="customPlaceholder" valueBinding="message"}}
|
||||
<div {{bindAttr class="customMessageLengthClasses"}}>{{customMessageLength}}</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -10,23 +10,47 @@ Discourse.FlagView = Discourse.ModalBodyView.extend({
|
|||
templateName: 'flag',
|
||||
title: Em.String.i18n('flagging.title'),
|
||||
|
||||
// trick to bind user / post to flag
|
||||
boundFlags: function(){
|
||||
var _this = this;
|
||||
var original = this.get('post.flagsAvailable');
|
||||
if(original){
|
||||
return $.map(original, function(v){
|
||||
var b = Discourse.BoundPostActionType.create(v);
|
||||
b.set('post', _this.get('post'));
|
||||
return b;
|
||||
});
|
||||
}
|
||||
}.property('post.flagsAvailable'),
|
||||
|
||||
changePostActionType: function(action) {
|
||||
|
||||
if (this.get('postActionTypeId') === action.id) return false;
|
||||
|
||||
this.get('boundFlags').each(function(f){
|
||||
f.set('selected', false);
|
||||
});
|
||||
action.set('selected', true);
|
||||
|
||||
this.set('postActionTypeId', action.id);
|
||||
this.set('isCustomFlag', action.is_custom_flag);
|
||||
this.set('selected', action);
|
||||
Em.run.next(function() {
|
||||
$("#radio_" + action.name_key).prop('checked', 'true');
|
||||
$('#radio_' + action.name_key).prop('checked', 'true');
|
||||
});
|
||||
return false;
|
||||
},
|
||||
|
||||
createFlag: function() {
|
||||
var actionType, _ref,
|
||||
_this = this;
|
||||
actionType = Discourse.get("site").postActionTypeById(this.get('postActionTypeId'));
|
||||
if (_ref = this.get("post.actionByName." + (actionType.get('name_key')))) {
|
||||
_ref.act({
|
||||
message: this.get('customFlagMessage')
|
||||
var _this = this;
|
||||
|
||||
var action = this.get('selected');
|
||||
var postAction = this.get('post.actionByName.' + (action.get('name_key')));
|
||||
|
||||
actionType = Discourse.get('site').postActionTypeById(this.get('postActionTypeId'));
|
||||
if (postAction) {
|
||||
postAction.act({
|
||||
message: action.get('message')
|
||||
}).then(function() {
|
||||
return $('#discourse-modal').modal('hide');
|
||||
}, function(errors) {
|
||||
|
@ -36,49 +60,24 @@ Discourse.FlagView = Discourse.ModalBodyView.extend({
|
|||
return false;
|
||||
},
|
||||
|
||||
customPlaceholder: (function() {
|
||||
return Em.String.i18n("flagging.custom_placeholder");
|
||||
}).property(),
|
||||
|
||||
showSubmit: (function() {
|
||||
var m;
|
||||
if (this.get("postActionTypeId")) {
|
||||
if (this.get("isCustomFlag")) {
|
||||
m = this.get("customFlagMessage");
|
||||
if (this.get('postActionTypeId')) {
|
||||
if (this.get('isCustomFlag')) {
|
||||
m = this.get('selected.message');
|
||||
return m && m.length >= 10 && m.length <= 500;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).property("isCustomFlag", "customFlagMessage", "postActionTypeId"),
|
||||
|
||||
customFlagMessageChanged: (function() {
|
||||
var len, message, minLen, _ref;
|
||||
minLen = 10;
|
||||
len = ((_ref = this.get('customFlagMessage')) ? _ref.length : void 0) || 0;
|
||||
this.set("customMessageLengthClasses", "too-short custom-message-length");
|
||||
if (len === 0) {
|
||||
message = Em.String.i18n("flagging.custom_message.at_least", { n: minLen });
|
||||
} else if (len < minLen) {
|
||||
message = Em.String.i18n("flagging.custom_message.more", { n: minLen - len });
|
||||
} else {
|
||||
message = Em.String.i18n("flagging.custom_message.left", { n: 500 - len });
|
||||
this.set("customMessageLengthClasses", "ok custom-message-length");
|
||||
}
|
||||
this.set("customMessageLength", message);
|
||||
}).observes("customFlagMessage"),
|
||||
}).property('isCustomFlag', 'selected.customMessageLength', 'postActionTypeId'),
|
||||
|
||||
didInsertElement: function() {
|
||||
var $flagModal;
|
||||
this.customFlagMessageChanged();
|
||||
this.set('postActionTypeId', null);
|
||||
$flagModal = $('#flag-modal');
|
||||
|
||||
// Would be nice if there were an EmberJs radio button to do this for us. Oh well, one should be coming
|
||||
// in an upcoming release.
|
||||
$("input[type='radio']", $flagModal).prop('checked', false);
|
||||
this.$("input[type='radio']").prop('checked', false);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -164,3 +164,11 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flag-modal {
|
||||
max-height: 450px;
|
||||
}
|
||||
|
||||
.custom-message-length {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
|
|
@ -73,6 +73,29 @@ class PostAction < ActiveRecord::Base
|
|||
|
||||
def self.act(user, post, post_action_type_id, message = nil)
|
||||
begin
|
||||
title, target_usernames,body = nil
|
||||
|
||||
if message
|
||||
[:notify_moderators, :notify_user].each do |k|
|
||||
if post_action_type_id == PostActionType.types[k]
|
||||
target_usernames = target_moderators(user)
|
||||
title = I18n.t("post_action_types.#{k}.email_title",
|
||||
title: post.topic.title)
|
||||
body = I18n.t("post_action_types.#{k}.email_body",
|
||||
message: message,
|
||||
link: "#{Discourse.base_url}#{post.url}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if target_usernames.present?
|
||||
PostCreator.new(user,
|
||||
target_usernames: target_usernames,
|
||||
archetype: Archetype.private_message,
|
||||
title: title,
|
||||
raw: body
|
||||
).create
|
||||
end
|
||||
create(post_id: post.id, user_id: user.id, post_action_type_id: post_action_type_id, message: message)
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
# can happen despite being .create
|
||||
|
@ -101,6 +124,10 @@ class PostAction < ActiveRecord::Base
|
|||
PostActionType.flag_types.values.include?(post_action_type_id)
|
||||
end
|
||||
|
||||
def is_private_message?
|
||||
post_action_type_id == PostActionType.types[:notify_user] ||
|
||||
post_action_type_id == PostActionType.types[:notify_moderators]
|
||||
end
|
||||
# A custom rate limiter for this model
|
||||
def post_action_rate_limiter
|
||||
return unless is_flag? || is_bookmark? || is_like?
|
||||
|
@ -169,4 +196,16 @@ class PostAction < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.target_moderators(me)
|
||||
User
|
||||
.where("moderator = 't' or admin = 't'")
|
||||
.where('id <> ?', [me.id])
|
||||
.select('username')
|
||||
.map{|u| u.username}
|
||||
.join(',')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -10,15 +10,15 @@ class PostActionType < ActiveRecord::Base
|
|||
|
||||
def types
|
||||
@types ||= Enum.new(:bookmark, :like, :off_topic, :inappropriate, :vote,
|
||||
:custom_flag, :spam)
|
||||
:notify_user, :notify_moderators, :spam)
|
||||
end
|
||||
|
||||
def auto_action_flag_types
|
||||
@auto_action_flag_types ||= flag_types.except(:custom_flag)
|
||||
@auto_action_flag_types ||= flag_types.except(:notify_user, :notify_moderators)
|
||||
end
|
||||
|
||||
def flag_types
|
||||
@flag_types ||= types.only(:off_topic, :spam, :inappropriate, :custom_flag)
|
||||
@flag_types ||= types.only(:off_topic, :spam, :inappropriate, :notify_user, :notify_moderators)
|
||||
end
|
||||
|
||||
def is_flag?(sym)
|
||||
|
|
|
@ -3,7 +3,8 @@ class PostActionTypeSerializer < ApplicationSerializer
|
|||
attributes :name_key, :name, :description, :long_form, :is_flag, :icon, :id, :is_custom_flag
|
||||
|
||||
def is_custom_flag
|
||||
object.id == PostActionType.types[:custom_flag]
|
||||
object.id == PostActionType.types[:notify_user] ||
|
||||
object.id == PostActionType.types[:notify_moderators]
|
||||
end
|
||||
|
||||
def name
|
||||
|
|
|
@ -66,7 +66,8 @@ class UserSerializer < BasicUserSerializer
|
|||
end
|
||||
|
||||
def stream
|
||||
UserAction.stream(user_id: object.id, offset: 0, limit: 60, guardian: scope)
|
||||
UserAction.stream(user_id: object.id, offset: 0, limit: 60,
|
||||
guardian: scope, ignore_private_messages: true)
|
||||
end
|
||||
|
||||
def can_edit
|
||||
|
|
|
@ -609,7 +609,6 @@ en:
|
|||
off_topic: "Undo flag"
|
||||
spam: "Undo flag"
|
||||
inappropriate: "Undo flag"
|
||||
custom_flag: "Undo flag"
|
||||
bookmark: "Undo bookmark"
|
||||
like: "Undo like"
|
||||
vote: "Undo vote"
|
||||
|
@ -617,7 +616,8 @@ en:
|
|||
off_topic: "{{icons}} marked this as off-topic"
|
||||
spam: "{{icons}} marked this as spam"
|
||||
inappropriate: "{{icons}} marked this as inappropriate"
|
||||
custom_flag: "{{icons}} flagged this"
|
||||
notify_moderators: "{{icons}} flagged this"
|
||||
notify_user: "{{icons}} started a private conversation"
|
||||
bookmark: "{{icons}} bookmarked this"
|
||||
like: "{{icons}} liked this"
|
||||
vote: "{{icons}} voted for this"
|
||||
|
@ -625,7 +625,8 @@ en:
|
|||
off_topic: "You flagged this as off-topic"
|
||||
spam: "You flagged this as spam"
|
||||
inappropriate: "You flagged this as inappropriate"
|
||||
custom_flag: "You flagged this for moderation"
|
||||
notify_moderators: "You flagged this for moderation"
|
||||
notify_user: "You started a private conversation with this user"
|
||||
bookmark: "You bookmarked this post"
|
||||
like: "You liked this"
|
||||
vote: "You voted for this post"
|
||||
|
@ -639,9 +640,12 @@ en:
|
|||
inappropriate:
|
||||
one: "You and 1 other flagged this as inappropriate"
|
||||
other: "You and {{count}} other people flagged this as inappropriate"
|
||||
custom_flag:
|
||||
notify_moderators:
|
||||
one: "You and 1 other flagged this for moderation"
|
||||
other: "You and {{count}} other people flagged this for moderation"
|
||||
notify_user:
|
||||
one: "You and 1 other started a private conversation with this user"
|
||||
other: "You and {{count}} other started a private conversation with this user"
|
||||
bookmark:
|
||||
one: "You and 1 other bookmarked this post"
|
||||
other: "You and {{count}} other people bookmarked this post"
|
||||
|
@ -661,9 +665,12 @@ en:
|
|||
inappropriate:
|
||||
one: "1 person flagged this as inappropriate"
|
||||
other: "{{count}} people flagged this as inappropriate"
|
||||
custom_flag:
|
||||
notify_moderators:
|
||||
one: "1 person flagged this for moderation"
|
||||
other: "{{count}} people flagged this for moderation"
|
||||
notify_user:
|
||||
one: "1 person started a private conversation with this user"
|
||||
other: "{{count}} started a private conversation with this user"
|
||||
bookmark:
|
||||
one: "1 person bookmarked this post"
|
||||
other: "{{count}} people bookmarked this post"
|
||||
|
@ -714,7 +721,8 @@ en:
|
|||
title: 'Why are you flagging this post?'
|
||||
action: 'Flag Post'
|
||||
cant: "Sorry, you can't flag this post at this time."
|
||||
custom_placeholder: "Why does this post require moderator attention? Let us know specifically what you are concerned about, and provide relevant links where possible."
|
||||
custom_placeholder_notify_user: "Why are you contacting this user privately?"
|
||||
custom_placeholder_notify_moderators: "Why does this post require moderator attention? Let us know specifically what you are concerned about, and provide relevant links where possible."
|
||||
custom_message:
|
||||
at_least: "enter at least {{n}} characters"
|
||||
more: "{{n}} to go..."
|
||||
|
|
|
@ -227,10 +227,18 @@ en:
|
|||
title: 'Inappropriate'
|
||||
description: 'This post contains content that a reasonable person would consider offensive, abusive, or hate speech.'
|
||||
long_form: 'flagged this as inappropriate'
|
||||
custom_flag:
|
||||
title: 'Other'
|
||||
notify_user:
|
||||
title: 'Notify {{username}}'
|
||||
description: 'This post contains something I want to talk to this user directly and privately about.'
|
||||
long_form: 'notified user'
|
||||
email_title: 'Regarding "%{title}"'
|
||||
email_body: "%{link}\n\n%{message}"
|
||||
notify_moderators:
|
||||
title: 'Notify moderators'
|
||||
description: 'This post requires general moderator attention based on the <a href="/faq">FAQ</a>, <a href="/tos">TOS</a>, or for another reason not listed above.'
|
||||
long_form: 'flagged this for moderation'
|
||||
long_form: 'notified moderators'
|
||||
email_title: 'Regarding "%{title}"'
|
||||
email_body: "%{link}\n\n%{message}"
|
||||
bookmark:
|
||||
title: 'Bookmark'
|
||||
description: 'Bookmark this post'
|
||||
|
|
|
@ -42,8 +42,15 @@ PostActionType.seed do |s|
|
|||
end
|
||||
|
||||
PostActionType.seed do |s|
|
||||
s.id = PostActionType.types[:custom_flag]
|
||||
s.name_key = 'custom_flag'
|
||||
s.id = PostActionType.types[:notify_user]
|
||||
s.name_key = 'notify_user'
|
||||
s.is_flag = true
|
||||
s.position = 7
|
||||
end
|
||||
|
||||
PostActionType.seed do |s|
|
||||
s.id = PostActionType.types[:notify_moderators]
|
||||
s.name_key = 'notify_moderators'
|
||||
s.is_flag = true
|
||||
s.position = 8
|
||||
end
|
||||
|
|
6
db/migrate/20130412015502_correct_counts_on_posts.rb
Normal file
6
db/migrate/20130412015502_correct_counts_on_posts.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
class CorrectCountsOnPosts < ActiveRecord::Migration
|
||||
def change
|
||||
rename_column :posts, :custom_flag_count, :notify_moderators_count
|
||||
add_column :posts, :notify_user_count, :integer, default: 0, null: false
|
||||
end
|
||||
end
|
6
db/migrate/20130412020156_correct_counts_on_topics.rb
Normal file
6
db/migrate/20130412020156_correct_counts_on_topics.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
class CorrectCountsOnTopics < ActiveRecord::Migration
|
||||
def change
|
||||
rename_column :topics, :custom_flag_count, :notify_moderators_count
|
||||
add_column :topics, :notify_user_count, :integer, default: 0, null: false
|
||||
end
|
||||
end
|
|
@ -294,6 +294,7 @@ class Guardian
|
|||
# You can only undo your own actions
|
||||
return false unless @user
|
||||
return false unless post_action.user_id == @user.id
|
||||
return false if post_action.is_private_message?
|
||||
|
||||
# Make sure they want to delete it within the window
|
||||
return post_action.created_at > SiteSetting.post_undo_action_window_mins.minutes.ago
|
||||
|
|
|
@ -13,6 +13,15 @@ describe PostAction do
|
|||
let(:post) { Fabricate(:post) }
|
||||
let(:bookmark) { PostAction.new(user_id: post.user_id, post_action_type_id: PostActionType.types[:bookmark] , post_id: post.id) }
|
||||
|
||||
describe "messaging" do
|
||||
it "sends an email to all moderators if selected" do
|
||||
PostAction.stubs(:create)
|
||||
PostAction.expects(:target_moderators).returns("bob")
|
||||
PostCreator.any_instance.expects(:create).returns(nil)
|
||||
PostAction.act(build(:user), build(:post), PostActionType.types[:notify_moderators], "this is my special message");
|
||||
end
|
||||
end
|
||||
|
||||
describe "flag counts" do
|
||||
before do
|
||||
PostAction.update_flagged_posts_count
|
||||
|
|
|
@ -101,6 +101,10 @@ Spork.each_run do
|
|||
Rails.cache.reconnect
|
||||
end
|
||||
|
||||
def build(*args)
|
||||
Fabricate.build(*args)
|
||||
end
|
||||
|
||||
# --- Instructions ---
|
||||
# Sort the contents of this file into a Spork.prefork and a Spork.each_run
|
||||
# block.
|
||||
|
|
Loading…
Reference in a new issue