Just wondering if anyone has any advice for this error?
It may be I’m doing a chunk of the Rails side of things wrong
This is happening when I refresh the page on a working part of the application.
Googling it brings up only its entry in the Discourse source
I need Rails Routes for the Thing I’m handling
Therefore I have to have a ThingController.rb ← is this true?
Currently ThingController.rb is very minimal, since the actual content here is a Topic List and is being rendered as such by Ember and TopicController.
class ThingController < ApplicationController
def index
end
def sent
end
end
Your controller inherits from the ApplicationController. In the application controller there are a number of methods that run on the before_action hook. This means they will run on every action in your controller (unless you skip them).
This particular before_action method is this
def check_xhr
# bypass xhr check on PUT / POST / DELETE provided api key is there, otherwise calling api is annoying
return if !request.get? && (is_api? || is_user_api?)
raise ApplicationController::RenderEmpty.new unless ((request.format && request.format.json?) || request.xhr?)
end
You will sometimes find in Discourse that this before_action method is skipped (a fair bit actually). In your case, you probably want to skip it on index. You would do by putting this at the top of your controller:
In a plugin you should always create an engine, and then put your custom classes on that. You’ll see this in all our open source plugins.
In your plugin.rb put a block that looks like this and change the names.
module ::DiscourseMachineLearning
class Engine < ::Rails::Engine
engine_name "discourse_machine_learning"
isolate_namespace DiscourseMachineLearning
end
end
Basically what you’re doing here is creating a seperate ROR environment in which your plugin can live. ROR relies on a lot of namespacing, so this is basically saying: 'this root namespace is mine I will put all my custom stuff here".
For route and controller namespacing, it works like this. First, you write some routes for your special namespace:
DiscourseMachineLearning::Engine.routes.draw do
get 'index' => "image#index"
end
Then you “mount” it onto the Discourse routes:
Discourse::Application.routes.append do
mount ::DiscourseMachineLearning::Engine, at: "ml"
end
In this setup
The path for “index” is /ml/index. “ml” is the mount point for the plugin’s engine, so it prepends all urls used by the plugin. “index” is the path given when the routes are drawn.
The full controller class is DiscourseMachineLearning::ImageController. All routes drawn in an engine have a controller class that starts with the engine module name. In "Image#index", the first part is the controller and the second is the controller “action” or method.
Cool this is super helpful.
I did originally have that code in an Engine, but decided to massively simplify it because I change the way the plugin worked such that it hooked much more into Discourse messages/topics functionality and so the Rails bit got a lot ‘thinner’. I figured it didn’t really need an Engine. But maybe it does.
Yup, so we will still need to use an engine, but we won’t draw the routes on the engine.
Using the Follow plugin as an example
Discourse::Application.routes.append do
%w{users u}.each_with_index do |root_path, index|
get "#{root_path}/:username/follow" => "follow/follow#index", constraints: { username: RouteFormat.username }
get "#{root_path}/:username/follow/following" => "follow/follow#list", constraints: { username: RouteFormat.username }
get "#{root_path}/:username/follow/followers" => "follow/follow#list", constraints: { username: RouteFormat.username }
end
end
The key here being follow/follow#index. This translates to
class Follow::FollowController < ApplicationController
def index
end
end
Note that the engine for this plugin is
module ::Follow
class Engine < ::Rails::Engine
engine_name "follow"
isolate_namespace Follow
end
end
So to use your plugin engine for your routes not on the plugin route mount (as they’re user routes), you just need to add the engine to the controller path, i.e. follow/follow