Ruby on Rails 3 - Reload lib directory for each re

2019-01-08 04:33发布

I'm creating a new engine for a rails 3 application. As you can guess, this engine is in the lib directory of my application.

However, i have some problems developing it. Indeed, I need to restart my server each time I change something in the engine.

Is there a way to avoid this ?

Can I force rails to completely reload the lib directory or a specific file and his requirements for each request ?

Thanks for your help :)

11条回答
走好不送
2楼-- · 2019-01-08 04:55

Worked for Rails 3.2.13 for reloading lib inside of gem of an app:

require_dependency 'the_class'

AND

config.autoload_paths += %W(#{config.root}/../fantasy/lib)

查看更多
甜甜的少女心
3楼-- · 2019-01-08 04:56

In RAILS 3, here's the secret sauce to auto-reload lib files. The code below's a bit overkill, for the example, but it's what I did to make it work. You can change the message in YoYo#gogo and see it on the screen each page load. Remove out the initializer and it stays the same.

/config/initializers/lib_reload.rb (new file)

ActiveSupport::Dependencies.explicitly_unloadable_constants << 'YoYo'
ActiveSupport::Dependencies.autoload_once_paths.delete(File.expand_path(File.dirname(__FILE__))+'/lib')

/lib/yo_yo.rb

class YoYo
  def gogo
    "OH HAI THERE"
  end
end

/app/controllers/home_controller

require 'yo_yo'
class HomeController < ApplicationController
  def index
    @message = YoYo.new.gogo
  end
end
查看更多
We Are One
4楼-- · 2019-01-08 04:57

Updated answer

I sum up all my findings on my blog, you better look there:

Old answer

I looked around for a solution for this too, and (for completeness' sake and also to point others into this direction) here is what I found.

As of Rails3.1, engines can easily be generated through the command rails plugin new my_plugin --full. This generates the skeleton for an engine.

--full means that the engine will be "merged" right into the including application, so that for example controllers should be directly accessible as if they were defined in the including app. This lets you e.g. have a helper file in my_engine/app/helpers/my_helper.rb that will be merged right into your including app's app/helpers/my_helper.rb helper.

There's another option --mountable which creates a namespace for the engine so that its controllers etc. will never collide with the including application's ones. This results in e.g. a helper being in my_engine/app/helpers/my_engine/my_helper.rb which won't collide with a helper app/helpers/my_helper.rb in your including app.

Now the more interesting part:

Within the generated engine's test folder, there's a dummy folder which holds a complete Rails application! What's it for?

When you develop an engine, its functionalities are meant to work completely on their own, and it should also be tested completely on its own. So it's the "wrong" way to develop an engine "within" another Rails app (though this intuitively often will feel right when extracting existing functionalities from a Rails app into an engine), and so theoretically it is also not needed to reload an engine's code with every request to the including application.

The "right" way seems to be this: develop and test your engine, as if it were a full Rails app using the dummy app! Therein you can do everything you can do in any "normal" Rails app, e.g. create controllers, models, views, etc. which use the functionalities the engine should provide. You also can normally start a server using rails s in your test/dummy directory and access the dummy app on localhost:3000, and when running your tests, the dummy app is automatically used for integration tests. Quite nice! :-)

You have to be careful where to put your stuff:

  • Any functionality that is meant to be used within another Rails app goes into my_engine/app, while any functionality that is only needed to test the engine's functionality goes into test/dummy/app.

Then afterwards you can easily load your engine in your main app's Gemfile like this: gem 'my_engine', :path => 'path/to/my_engine' or publish it to GitHub as a gem.

(One interesting thing (and to come back to this topic's subject) is that when I start the dummy's server, then all changes in the engine seem to be reflected within it! So somehow it seems to be possible to include an engine within a Rails app without caching it...? I don't know how this happens.)

So to sum up: an engine provides functionality that can stand completely on its own, so it should also be developed and tested on its own. Then, when it has reached a stable state, it can be included by any other app that needs its functionality.

Here's some resources I find useful:

I hope you find this answer useful. I'm still very new to engines in general, so if there's any wrong information, please tell me, and I'll correct it.

查看更多
手持菜刀,她持情操
5楼-- · 2019-01-08 05:04

I couldn't get any of the above to work for me so I dug in the Rails code a bit and came up with this:

New file: config/initializers/reload_lib.rb

if Rails.env == "development"
  lib_reloader = ActiveSupport::FileUpdateChecker.new(Dir["lib/**/*"]) do
    Rails.application.reload_routes! # or do something better here
  end

  # For Rails 5.1+
  ActiveSupport::Reloader.to_prepare do
    lib_reloader.execute_if_updated
  end

  # For Rails pre-5.1 
  ActionDispatch::Callbacks.to_prepare do
    lib_reloader.execute_if_updated
  end

end

Yes, I know it's disgusting but it's a hack. There might be a better way to trigger a full reload, but this works for me. My specific use case was a Rack app mounted to a Rails route so I needed it to reload as I worked on it in development.

Basically what it does is it checks if any files in /lib have changed (modified timestamp) since last loaded and then triggers a reload if they change.

I might also mention I have this in my config/application.rb

config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]

Which just by default makes sure everything in my lib directory gets loaded.

Yays!

查看更多
贪生不怕死
6楼-- · 2019-01-08 05:04

Here is my version inspired from @pbhogan's answer that reloads all the ruby files in your rails /lib directory when any of those files is changed.

It also silences warnings to avoid messages regarding already initialized constants.

Works as of Rails 3.2.8

if Rails.env.development?

  lib_ruby_files = Dir.glob(File.join("lib/**", "*.rb"))
  lib_reloader ||= ActiveSupport::FileUpdateChecker.new(lib_ruby_files) do
    lib_ruby_files.each do |lib_file|
      silence_warnings { require_dependency(lib_file) }
    end
  end

  ActionDispatch::Callbacks.to_prepare do
    lib_reloader.execute_if_updated
  end

end
查看更多
登录 后发表回答