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:

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

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