Search code examples
ruby-on-railsrubygemsactiveadmingemfile

Using rails inside gem as a dependency


While browsing through the active-admin code, I found that Gemfile contains the rails entry.

rails_version = detect_rails_version
gem 'rails', rails_version

Does that mean active-admin internally creates a rails server which is not the main rails app?

If Yes, will that lead to performance issues and using it with complex rails applications is a bad practice?

if No, what is the use of it in active-admin Gemfile?

Googled a lot with different keywords which only leads to gem install and related results.

I would be glad if somebody put some insights. Thanks :)

update1: After Gaurish answer and googling, it seems that Gemfile inside gem is used to list dependencies needed for gem internally. But it create another question, what happens when application gems and gems' gem conflits?

update2: Can we use active-admin standalone, since it has rails as a dependency? (Just installing active-admin by gem install active-admin instead of keeping it in some other rails application.)


Solution

  • No. This seems a bundler issue. this code just declares a dependency on rails. sound's strange? Here is what I think is happening.

    the usual way is to declare your dependencies in .gemspec file by using following line:

    s.add_dependency("rails", ">= 3.0.0")
    

    the above lines add a dependancy on rails v3.0 & above(v3.1,v3.2). now this works fine for other gems & bundler will auto resolve dependencies. But when you try to support multiple versions of rails, bundler might get confused.

    Ideally, bundler should be able to automatically bundle(install) all the dependencies of our gem. And also dependencies of gem dependencies. example, bundle should be smart enough to figure out if rails v3.1 or v3.2 is required, it also needs to include sass-rails & 'uglifier' as they required by rails 3.1 & 3.2. But if rails v3.0 is required, nothing extra need to be done.

    But we live in not so perfect world, so bundler is not smart enough. Hence, I think this is the reason active-admin has to resort to the following hack around bundler's shortcomings.

    unless defined?(RAILS_VERSION_FILE)
      RAILS_VERSION_FILE = File.expand_path("../../../.rails-version", __FILE__)
    end
    
    unless defined?(DEFAULT_RAILS_VERSION)
      DEFAULT_RAILS_VERSION = "3.1.0"
    end
    
    def detect_rails_version
      return DEFAULT_RAILS_VERSION unless File.exists?(RAILS_VERSION_FILE)
      File.read(RAILS_VERSION_FILE).chomp
    end
    
    def write_rails_version(version)
      File.open(RAILS_VERSION_FILE, "w+"){|f| f << version }
    end
    
    
    rails_version = detect_rails_version
    gem 'rails', rails_version
    
    case rails_version
    when /^3\.0/
      # Do nothing, bundler should figure it out
    when /^3\.(1|2)/
      # These are the gems you have to have for Rails 3.1 to be happy
      gem 'sass-rails'
      gem 'uglifier'
    else
      raise "Rails #{rails_version} is not supported yet"
    end
    

    if you notice the above code, it checks if current version of rails is v3.1 or v3.2. if yes, then add new two as dependencies. That's all this code does.


    [Update]

    Questions 1. what happens when application gems and gems' gem conflicts? If they conflict in such a way that bundler can't find a compatible version that satisfies dependencies of your app and gems. bundler will fail with an error something similar to this:

    Bundler could not find compatible versions for gem "json":
      In Gemfile:
        chef (~> 10.26) ruby depends on
          json (<= 1.7.7, >= 1.4.4) ruby
    
        berkshelf (~> 2.0) ruby depends on
          json (1.8.0)
    

    Question 2. Can we use active-admin standalone without rails? No. when you do gem install active-admin, rubygems will auto install rails as its listed as a dependency of this gem in gemspec file.