Transforming simple ruby files into a ruby gem

Hey,

i am trying to create my first plugin for discourse. I already have the ruby code working fine. I have 4 different ruby files with different kind of methods in it, i like to have them splitted. And a Gemfile and a Gemfile.lock
How can i transform them now into a Discourse Plugin?
I already read the Plugin Beginner Tutorial, but it focused on JavaScript Code and not so much on pure ruby code i guess? I am just not sure where to put my files. Is it possible to let them split up and copy the code of the ā€œmainā€ file into the plugin.rb ?
I didnt use any kind of class definitions or something like that just tried to split different functionality in different files and methods.
Where do i have to put the Gemfile and the Gemfile.lock

Tried it local but it didnt find the twitter gem which is necessary for it to work, i dont know where the issue came from.

2 Likes

Glad to here you are working on your first plugin :slight_smile:. I would look at the examples of other plugins and see how they do the ruby side of things.

I donā€™t think your plugin actually needs to have a Gemfile, but you need to specify all of your dependencies and sub-dependencies inside of your plugin.rb file. Discourse will then install all of the gems when it loads the plugin. See this example:

https://github.com/xfalcox/discourse-backups-to-dropbox/blob/master/plugin.rb

Any files your plugin uses you can put inside of the ā€œlibā€ folder and then require them inside of plugin.rb.

https://github.com/discourse/discourse-data-explorer/blob/master/plugin.rb#L579

9 Likes

Yeah thanks for your response!
I tried to follow all the advices and format the dependencies like the other plugins.

But i always get that kind of error when i try to start the rails server:

/.rvm/gems/ruby-2.5.1/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:inrequireā€™: cannot load such file ā€“ twitter (LoadError)`

i required twitter in the files where i need it put this in the normal plugin.rb

gem "bundler"
gem "net"
gem "twitter"

(obviously there are more gems i put in like this, thats just for an example)
Do you have any idea why its not working?
I also tried to specify the version of the gem like that gem "twitter", "6.2" and add the required false statement, but still the same error.
Maybe good to know, its a local discourse server

I think i just dont get how the gems are working in the plugin environment, cause i always get errors regarding them, for example i also get: you are specifying gem i18n in ā€¦ however it does not exist ā€¦

Not exactly, but I still think it is dependency issues and not specifying the gems correctly.

I think it has to be the full version number like 6.2.0. I also donā€™t think you need to specify ā€œbundlerā€ or ā€œnetā€.

Apparently when writing a plugin you have to go through dependency hell :fire: to get it all working and happy. Basically the workflow I go though is start with the main gem that I want. In this case it is the twitter gem. Make sure this one is always listed as the last gem in your plugin.rb file.

gem 'twitter', '6.2.0',  { require: false }

Then I look at all the runtime dependencies for this gem and ONE AT A TIME list each dependency with each subdependency above the twitter gem. Run bundle exec rails s between each dependency and check for (Gem::MissingSpecError) to know which dependency to add next.

Apparently the addressable gem (2.5.2, instead of 2.3.8) is already required by a discourse gem, so it should be safe to ignore. (You can run gem dependency addressable --reverse-dependencies to see which gems require it).

Your plugin.rb should look something like:

gem 'buftok',         '0.2.0',        { require: false }
gem 'equalizer',      '0.0.11',       { require: false }
gem 'domain_name',    '0.5.20180417', { require: false }
gem 'http-cookie',    '1.0.3',        { require: false }
gem 'http-form_data', '2.0.0',        { require: false }
gem 'http_parser.rb', '0.6.0',        { require: false }
gem 'http',           '3.3.0',        { require: false }
gem 'memoizable',     '0.4.2',        { require: false }
gem 'naught',         '1.1.0',        { require: false }
gem 'simple_oauth',   '0.3.1',        { require: false }
gem 'twitter',        '6.2.0',        { require: false }

#gem 'addressable',    '2.3.8', { require: false } # Already required in Discourse
#gem 'multipart-post', '2.0.0', { require: false } # Already required in Discourse

require 'twitter'

client = Twitter::REST::Client.new do |config|
  config.consumer_key        = "YOUR_CONSUMER_KEY"
  config.consumer_secret     = "YOUR_CONSUMER_SECRET"
  config.access_token        = "YOUR_ACCESS_TOKEN"
  config.access_token_secret = "YOUR_ACCESS_SECRET"
end

bundle exec rails s should now run successfully and you can now write your plugin and break things out into separate files if youā€™d like, but the gem dependencies should stay in the plugin.rb file.

4 Likes

Thank you so much, honestly i have no clue why its working now, maybe i had a few dependencys wrong or in a wrong order, but now it works totally fine!
A quick follow up question, what would you recommend to automize the file? So for example i want to have it run every 10 secs or something like that.

Update:

Now i use sidekiq to run my main file.

after_initialize do
class MainPlugin
include Sidekiq

require_relative File.expand_path(ā€™ā€¦/lib/gen_all_posts_url.rbā€™, FILE)
require_relative File.expand_path(ā€™ā€¦/lib/get_tweets.rbā€™, FILE)
##morecode

end
end

Sidekiq::Cron::Job.create(name: ā€˜MainPlugin every minuteā€™, cron: ā€™ *ā€™, class:ā€˜MainPluginā€™)

But its not working, the strange thing about it is, that i tried for class:ā€˜plugin.rbā€™ once, which was obviously wrong - so i switched it back, but i am still getting the error message:

Job exception: wrong constant name plugin.rb

I dont get what i am doing wrong here