Configuring Ruby with IntelliJ/RubyMine - ruby

I am (almost) new to Ruby and try to configure IntelliJ so it helps me developing Jekyll plugins. I am on a Mac and I installed Ruby with RVM. I am willing to move on to Homebrew or whatever.
Jekyll is a static site generator. The plugins for Jekyll should reside inside _plugins which I did.
I added Ruby to the project settings.
The problem now is, IntelliJ tells me I have multiple declarations of in example "require". This happens pretty often, in example for "gem" or "File".
Some of the dependencies do not seem to be recognized as well.
Here is my Gem file:
source 'https://rubygems.org'
gem 'jekyll', '>=3.0.0'
gem 'i18n', '>=0.7.0'
gem 'redcarpet'
gem 'pygments.rb'
I would have expected that - based on Jekyll dependencies - in example this would have been recognized:
Liquid::Template.register_filter( ... )
But "Liquid" is not found.
The thing is, the code itself works when I execute it.
But I cannot easily jump into "Array" or the dependency "Jekyll" with clicking on the terms. The "Jekyll" module is even not found, while "Array" is only duplicate.
Here is my classpath for my project:
For the record, I did "bundle install".
Any ideas what I can do better to make my setup smooth and clean?

Related

How to pick gems from a specific directory

I forked a gem I use a lot in order to code some enhancements on it. I want it to be installed in a subfolder called ~/codebase/ruby, where I keep all my Ruby projects, arranged in subdirectories.
To do that, I built and installed the gem with the following commands:
gem build my_gem.gemspec
gem install mygem-x.x.gem -i./mygem
mygem is installed in ~/codebase/ruby/mygem. I can't get my client code (which is in another directory) to grab mygem from there.
I've tried all of the following without success:
Running ruby with argument -I<path_to_mygem>.
Adding <path_to_mygem> to PATH.
Putting :gempath: <path_to_mygem> into ~/.gemrc.
I know I could put the gem in ~/.gem/ruby/<version> (since it appears in gem env) and that would probably work, but that would break my existing directory structure of Ruby code, forcing me to code in a different directory only for mygem, which is something I want to avoid unless it's the only option.
Thoughts?
Hopefully you are using Bundler to load the required gems. Then you just specify the path to the gem file in your Gemfile:
gem 'my_gem', :path => '~/codebase/ruby/mygem'

Calling another Gem as a program from my Gem

I'm working on a command line tool (a ruby gem) to quickly and easily generate custom jekyll repositories from some basic user input for my company. I would like to add a feature where the gem can automatically build the repo and output the finished directory, but the jekyll gem doesn't seem to provide any internal ruby interface to do that, at least not one that would be easy to implement.
Is there a way I could have my gem call their gem as a program, i.e. system 'jekyll build', but without having to worry about whether or not the user has the jekyll gem installed?
You can add jekyll to your gems list of dependencies, by adding the following to your gem's .gemspec file. That means whenever someone installs your gem, jekyll would also be installed.
spec.add_dependency 'jekyll'

build a ruby gem and conditionally specify dependencies

I am working on a gem that has needs to set dependencies conditionally when the gem is installed. I've done some digging around
and it looks like i'm not alone in this need.
Rubygems: How do I add platform-specific dependency?
this is a long thread
http://www.ruby-forum.com/topic/957999
The only way I can see to add dependencies to a gem is to use add_dependency method within a Gem::Specifiction block in a .gemspec file
Gem::Specification.new do |s|
# ... standard setup stuff
# conditionally set dependencies
s.add_dependency "rb-inotify", "~> 0.8.8" if RUBY_PLATFORM =~ /linux/i
s.add_dependency "rb-fsevent", "~> 0.4.3.1" if RUBY_PLATFORM =~ /darwin/i
s.add_dependency "rb-fchange", "~> 0.0.5" if RUBY_PLATFORM =~ /mswin|mingw/i
end
Based on all of the docs and threads I found on the net, I would have expected that if you install the gem on
Linux, then, rb-inotify would be a dependency and auto-installed
Mac - rb-fsevent would be installed
Windows - rb-fchange would be installed
However, it seems that is not the case. The "if" statements within the block are evaluated at the time the gem is built and packaged. Therefore,
if you build and package the gem on Linux, then, rb-inotify is added as a dependency, Mac, then, rb-fsevent, Windows - rb-fchange.
Still needing a solution, I dug around in the rubygems code and it seems the following is a broad stoke of what happens.
build all of your code for your gem: foo.gem
create a foo.gemspec file
build, package, and release the gem to a gem server such as rubygems.org
let everyone know
developers install it locally via: gem install foo
the foo.gem file is downloaded, unpacked, and installed. all dependencies are installed as well.
everything should be set and we can beging using the gem.
It seems that when the gem is built and released the foo.gemspec file is loaded and the Gem::Specification block is evaluated and converted to YAML, compressed as
metadata.gz, and included in foo.gem. The ruby code is compressed into data.tar.gz and included as well. When the gem is installed on the local developer machine,
the YAML is extracted from metadata.gz and converted back into a Gem::Specification block, however, it is not converted back to the original block.
instead, you will see something like the following:
Gem::Specification.new do |s|
if s.respond_to? :specification_version then
s.specification_version = 3
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<rb-inotify>, ["~> 0.8.8"])
else
s.add_dependency(%q<rb-inotify>, ["~> 0.8.8"])
end
else
s.add_dependency(%q<rb-inotify>, ["~> 0.8.8"])
end
end
Ok. So, I have a bird's eye view of the process, however, that does not change my desire to build a single gem and conditionally specify dependencies for a range of OS targets.
If anyone has a solution other than building multiple .gemspec files for each target OS... I'm all ears!!
I have also stumbled upon this problem in the past. The only workaround I could find was to create a Rake task for installing the dependencies. Of course, at that stage, you might just want to let the user figure out on his own which gem he is missing based on the error message he is receiving. In my case, there were several platform-dependent dependencies to be installed, so that wasn't an option.
Rakefile:
task :install do |t|
require './lib/library/installer'
Library::Installer.install
end
Installer:
module Library::Installer
require 'rubygems/dependency_installer'
def self.install
installer = Gem::DependencyInstaller.new
dependency = case RUBY_PLATFORM
when /darwin/i then ["rb-fsevent", "~> 0.4.3.1"]
when /linux/i then ["rb-inotify", "~> 0.8.8"]
when /mswin|mingw/i then ["rb-fchange", "~> 0.0.5"]
end
installer.install(*dependency)
end
Then, the user can use rake install to get install appropriate dependencies.
Conditional dependency install (not just based on platform, but based on user input, for example) is cruelly missing to RubyGems. Let's hope it'll get implemented in the future!
i have never done this myself, but there are some gems that are available in platform specific versions: http://rubygems.org/gems/libv8/versions
from what i understand it's just a naming thing, which can be configured by setting the platform option of your gemspec. have a look at the doc: http://guides.rubygems.org/specification-reference/#platform=
I have looked into this as well and have come to the conclusion that is not possible by design. Having a single 'mega gem' for all platforms causes the problem of not knowing if a platform is supported until the gem is downloaded and installed. A Gem would have to be smart enough to determine what is correct way to install depending on the platform. If a platform is not supported at all, the gem may fail horribly, opening a big can of worms. There use to be a callback after a gem was installed that was removed for the same reason, no magic to get a gem to install correctly. Some people have hacked around this using mkmf, but I suggest following the worn path of a gem per platform as the better solution.
Based on this, in a project that builds a gem for ruby and jruby, I have to manually create each gem and upload them to RubyGem. Using Jeweler this is as simple as specifing the Gemfile, but I have to rebuild the gem spec each time I package a gem. Fairly trivial when supporting only 2 platforms, but the build process is straight forward enough that it could be automated to provide support multiple platform gems.

How to create a new Ruby gem?

To create a new Ruby gem, should I use Jeweler or should I use Bundler's built-in gem skeleton to create a base gem? What are the differences that matter?
Use Bundler
From the command line:
bundle gem your_new_gem
This will create a directory called your_new_gem with just a basic set of files and directory structure that are now considered best-practice. It's quick, easy, and a great place to start.
Creating a Gem isn't that difficult and I would advise to try building a gem from scratch, without any tools. After you know what's involved (creating a gemspec, building and pushing it to rubygems.org), you can use tools to speed up the process. My guess is you won't because making a gem is hardly the trouble at all.
I would go with Jeweler. The Bundler skeleton is only going to give you the basics. Jeweler has alot more options to work with and many helpful rake tasks for versioning, pushing to github, creating the gemspec, building and installing.
If you are working with Rails 3 engines, I have a Jeweler fork (definitely a work-in-progress) that will generate the app skelaton and include the engine file. You just have to run the jeweler command with --rails3-engine as an option. Here is the fork if you are interested:
https://github.com/johnmcaliley/jeweler
I would recommend using the built-in bundler command.
bundle gem your_gem_name
There are some rules that you should follow when creating a gem. Such as naming conventions and versioning rules.
I recently wrote a post on creating gems in netguru's blog. I think you'll find what you need in there.
https://netguru.co/blog/posts/creating-a-gem-a-step-by-step-tutorial
Hope this helps.
Here's an alternative that's worth looking at: ore
Bundler gives you a single template for ruby gems, whereas ore has multiple built in templates, plus the ability to create your own. It also supports Git, SVN (urgh) and Mercurial.
You can build a gem in RubyMine too. File > New Project > New Gem. It is that easy. But I want to make some notes about this approach:
For debugging, RubyMine will use the Fast Debugger gem, ruby-debug-ide. I know that most people now are using Pry with Byebug, but ruby-debug-ide is an interface which glues ruby-debug to IDEs like Eclipse (RDT), NetBeans and RubyMine.
Under Run > Edit Configurations > + > Ruby, I add a new debug configuration, according to the documentation here: https://www.jetbrains.com/help/ruby/run-debug-configuration-gem-command.html#1
Under Configuration, under 'Ruby Script', I add the path to the ruby gem file under lib: lib/my_gem.rb
Under Configuration, under 'Ruby SDK', I specify an RVM gemset I am using.
Under Bundler section, I check 'Run the script in context of bundler'. This would use bundle exec, which will read the dependencies in my Gemfile in my project's root. Now for gems, the Gemfile contains a method call "gemspec", which in turn reads the dependencies in dependencies in my_gem.gemspec. There, I have dependencies passed to the Gem::Specification.new block:
spec.add_development_dependency "bundler", "~> 1.7"
spec.add_development_dependency "rake", "~> 10.0"

unpacking/freezing gems into a non-rails ruby app

I'm writing a non-Rails ruby application (gasp!) and would like to be able to include all the gem dependencies which the application requires in a vendor subdirectory. This would be similar to how http://gemsonrails.rubyforge.org/ works for Rails apps.
The goal here is to avoid the situation my team currently experiences when a new dependency is added. Every developer on my team has to install the gem manually, and then someone has to manually update each test and staging and production machine. If we can freeze the dependencies into the distributed application itself then a simple svn update (or git pull for those hipsters in the crowd) would be all that is needed.
UPDATE (New Solution):
Try Yehuda Katz's new bundler gem. gem install bundler then create a Gemfile with all your dependencies. See the documentation for more info.
Old Recommendation:
One easy way is to just manually unpack the gems into your vendor directory and add the lib path of the unpacked gems to the front of the $LOAD_PATH.
To unpack a gem:
$ cd vendor/gems
$ gem unpack active_support
Unpacked gem: '/path/to/myproject/vendor/gems/activesupport-2.3.2'
Just make sure you unpack all the necessary gems and their dependencies (using the correct versions).
To add all gems under vendor/gems to your $LOAD_PATH, try adding something like this to your application's initialization:
Dir.glob(File.join("vendor", "gems", "*", "lib")).each do |lib|
$LOAD_PATH.unshift(File.expand_path(lib))
end
Update: Sarah (in the comments) convinced me it might also be necessary to override the GEM_PATH. Here's one way to do that:
require 'rubygems'
gem_paths = [File.expand_path(File.join("vendor", "gems")), Gem.default_dir]
Gem.clear_paths
Gem.send :set_paths, gem_paths.join(":")
Another option is to look into Rip (Ruby’s Intelligent Packaging) for managing your dependencies. Rip looks really sweet, but it's still new.

Resources