excsm
2012-Sep-25 13:58 UTC
[activerecord] Better loading pattern for connection adapters that play nicely with bundler --standalone
Hi, I initially sent this idea as a github issue: https://github.com/rails/rails/issues/7753 (closed) Steve Klabnik suggested I should post here first (better late than never). I''ve since created a PR: https://github.com/rails/rails/pull/7755 Original discussion: ============= I''m using the relatively new --standalone feature/option of bundler so as to avoid extracting a part of my rails project into a separate project. My only requirement is to not have to load rails entirely: By creating a custom gemfile (subset of your typical gemfile) and executing: bundle install --gemfile=Gemfile.my_subset --standalone bundler will create a ruby script which requires all the gems in your gemfile subset using the original gems locations. e.g. : path = File.expand_path(''..'', __FILE__) $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/rake-0.9.2.2/lib") $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/multi_json-1.0.4/lib") $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/activesupport-3.1.6/lib") $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/builder-3.0.3/lib") $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/i18n-0.6.0/lib") $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/activemodel-3.1.6/lib") $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/arel-2.2.3/lib") $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/tzinfo-0.3.33/lib") $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/activerecord-3.1.6/lib") ... This allows scripts to e.g. load just activerecord without setting up a separate gemfile. The issue here is that with this setup script, you aren''t using bundler (to avoid loading rails and the gems in your typical gemfile.). Additionally, the gems the script requires are `expanded` gems and have not been installed via `gem install`. Activerecord connection adapters have this code: gem ''mysql2'', ''~> 0.3.10'' require ''mysql2'' This code assumes: * You''re running the code within bundler (recommended) * You have the mysql2 gem installed as a gem in your system When using bundler standalone neither of these cases are true so the app breaks with: Please install the mysql2 adapter: `gem install activerecord-mysql2-adapter` (no such file to load -- active_record/connection_adapters/mysql2_adapter) because it hits the ''gem'' call before it attempts to require ''mysql2'' IMHO a better approach would be: begin require ''mysql2'' rescue LoadError gem ''mysql2'', ''~> 0.3.10'' require ''mysql2'' end This code assumes you already have mysql2 on your load path somehow and only if it raises an exception do we attempt to issue the `gem` call. Is this reasonable? Regards, Saimon -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To view this discussion on the web visit https://groups.google.com/d/msg/rubyonrails-core/-/daUdB5JI3hIJ. To post to this group, send email to rubyonrails-core@googlegroups.com. To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.
Aaron Patterson
2012-Sep-25 17:33 UTC
Re: [activerecord] Better loading pattern for connection adapters that play nicely with bundler --standalone
On Tue, Sep 25, 2012 at 06:58:45AM -0700, excsm wrote:> Hi, > > I initially sent this idea as a github > issue: https://github.com/rails/rails/issues/7753 (closed) > > Steve Klabnik suggested I should post here first (better late than never). > > I''ve since created a PR: https://github.com/rails/rails/pull/7755 > > Original discussion: > =============> > > I''m using the relatively new --standalone feature/option of bundler so as > to avoid extracting a part of my rails project into a separate project. My > only requirement is to not have to load rails entirely: > > By creating a custom gemfile (subset of your typical gemfile) and executing: > > bundle install --gemfile=Gemfile.my_subset --standalone > > bundler will create a ruby script which requires all the gems in your > gemfile subset using the > original gems locations. > > e.g. : > > path = File.expand_path(''..'', __FILE__) > $:.unshift > File.expand_path("#{path}/../ruby/1.9.1/gems/rake-0.9.2.2/lib") > $:.unshift > File.expand_path("#{path}/../ruby/1.9.1/gems/multi_json-1.0.4/lib") > $:.unshift > File.expand_path("#{path}/../ruby/1.9.1/gems/activesupport-3.1.6/lib") > $:.unshift > File.expand_path("#{path}/../ruby/1.9.1/gems/builder-3.0.3/lib") > $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/i18n-0.6.0/lib") > $:.unshift > File.expand_path("#{path}/../ruby/1.9.1/gems/activemodel-3.1.6/lib") > $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/arel-2.2.3/lib") > $:.unshift > File.expand_path("#{path}/../ruby/1.9.1/gems/tzinfo-0.3.33/lib") > $:.unshift > File.expand_path("#{path}/../ruby/1.9.1/gems/activerecord-3.1.6/lib") > ... > > This allows scripts to e.g. load just activerecord without setting up a > separate > gemfile. > > The issue here is that with this setup script, you aren''t using bundler (to > avoid loading rails and > the gems in your typical gemfile.). Additionally, the gems the script > requires are `expanded` gems > and have not been installed via `gem install`. > > Activerecord connection adapters have this code: > > gem ''mysql2'', ''~> 0.3.10'' > require ''mysql2'' > > This code assumes: > > * You''re running the code within bundler (recommended) > * You have the mysql2 gem installed as a gem in your system > > When using bundler standalone neither of these cases are true so the app > breaks with: > > Please install the mysql2 adapter: `gem install > activerecord-mysql2-adapter` (no such file to load -- > active_record/connection_adapters/mysql2_adapter) > because it hits the ''gem'' call before it attempts to require ''mysql2'' > > IMHO a better approach would be: > > begin > require ''mysql2'' > rescue LoadError > gem ''mysql2'', ''~> 0.3.10'' > require ''mysql2'' > end > > This code assumes you already have mysql2 on your load path somehow and > only if it raises an exception do we attempt to issue the `gem` call.If you have mysql2 on your system, the `gem` call will never be made (defeating the purpose of the `gem` call in the first place).> Is this reasonable?If we don''t use the `gem` command, we can''t enforce a particular version of mysql2 in the adapter code. Since AR has "soft dependencies" (dependencies that are not declared in the gemspec), we need some way to ensure that we''ve loaded a connection handling gem that has the version we actually support. Doing a `require ''mysql2''` could pick up *any* version of mysql2, including one that won''t actually work with the AR adapter code. -- Aaron Patterson http://tenderlovemaking.com/
Saimon Moore
2012-Sep-27 09:10 UTC
Re: [activerecord] Better loading pattern for connection adapters that play nicely with bundler --standalone
Hi Aaron, On 25/09/2012, at 19:33, Aaron Patterson <tenderlove@ruby-lang.org> wrote:> On Tue, Sep 25, 2012 at 06:58:45AM -0700, excsm wrote: >> Hi, >> >> I initially sent this idea as a github >> issue: https://github.com/rails/rails/issues/7753 (closed) >> >> Steve Klabnik suggested I should post here first (better late than never). >> >> I''ve since created a PR: https://github.com/rails/rails/pull/7755 >> >> Original discussion: >> =============>> >> >> I''m using the relatively new --standalone feature/option of bundler so as >> to avoid extracting a part of my rails project into a separate project. My >> only requirement is to not have to load rails entirely: >> >> By creating a custom gemfile (subset of your typical gemfile) and executing: >> >> bundle install --gemfile=Gemfile.my_subset --standalone >> >> bundler will create a ruby script which requires all the gems in your >> gemfile subset using the >> original gems locations. >> >> e.g. : >> >> path = File.expand_path(''..'', __FILE__) >> $:.unshift >> File.expand_path("#{path}/../ruby/1.9.1/gems/rake-0.9.2.2/lib") >> $:.unshift >> File.expand_path("#{path}/../ruby/1.9.1/gems/multi_json-1.0.4/lib") >> $:.unshift >> File.expand_path("#{path}/../ruby/1.9.1/gems/activesupport-3.1.6/lib") >> $:.unshift >> File.expand_path("#{path}/../ruby/1.9.1/gems/builder-3.0.3/lib") >> $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/i18n-0.6.0/lib") >> $:.unshift >> File.expand_path("#{path}/../ruby/1.9.1/gems/activemodel-3.1.6/lib") >> $:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/arel-2.2.3/lib") >> $:.unshift >> File.expand_path("#{path}/../ruby/1.9.1/gems/tzinfo-0.3.33/lib") >> $:.unshift >> File.expand_path("#{path}/../ruby/1.9.1/gems/activerecord-3.1.6/lib") >> ... >> >> This allows scripts to e.g. load just activerecord without setting up a >> separate >> gemfile. >> >> The issue here is that with this setup script, you aren''t using bundler (to >> avoid loading rails and >> the gems in your typical gemfile.). Additionally, the gems the script >> requires are `expanded` gems >> and have not been installed via `gem install`. >> >> Activerecord connection adapters have this code: >> >> gem ''mysql2'', ''~> 0.3.10'' >> require ''mysql2'' >> >> This code assumes: >> >> * You''re running the code within bundler (recommended) >> * You have the mysql2 gem installed as a gem in your system >> >> When using bundler standalone neither of these cases are true so the app >> breaks with: >> >> Please install the mysql2 adapter: `gem install >> activerecord-mysql2-adapter` (no such file to load -- >> active_record/connection_adapters/mysql2_adapter) >> because it hits the ''gem'' call before it attempts to require ''mysql2'' >> >> IMHO a better approach would be: >> >> begin >> require ''mysql2'' >> rescue LoadError >> gem ''mysql2'', ''~> 0.3.10'' >> require ''mysql2'' >> end >> >> This code assumes you already have mysql2 on your load path somehow and >> only if it raises an exception do we attempt to issue the `gem` call. > > If you have mysql2 on your system, the `gem` call will never be made > (defeating the purpose of the `gem` call in the first place). > >> Is this reasonable? > > If we don''t use the `gem` command, we can''t enforce a particular version > of mysql2 in the adapter code. Since AR has "soft dependencies" > (dependencies that are not declared in the gemspec), we need some way to > ensure that we''ve loaded a connection handling gem that has the version > we actually support. Doing a `require ''mysql2''` could pick up *any* > version of mysql2, including one that won''t actually work with the > AR adapter code.I understand though I think it sucks… What about something like: begin gem ''mysql2'', ''~> 0.3.11'' require ''mysql2'' rescue LoadError puts "You do not appear to have mysql2 (~> 0.3.11) installed. AR requires this to function correctly." begin require ''mysql2'' puts "Found some version of mysql2. Proceeding..." rescue LoadError => err raise err end end Then you leave the responsibility up to the developer…> > -- > Aaron Patterson > http://tenderlovemaking.com/Saimon -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com. To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.