如何从外部访问宝石化的Padrino Apps模型(不是在控制器中,而是例如独立的脚本)

时间:2014-10-06 09:54:16

标签: ruby gem padrino

我有一个名为Gusy的Padrino应用程序指定(续集)模型,如

# gusy/models/seminar.rb
class Seminar < Sequel::Model
  # hopefully irrelevant stuff defined here
end

我想从第二个gem或bin/中的脚本访问此模型。

现在,例如我要求Gusy来自第二个宝石“gusy_fill”。 Gemfile用于设置Gusy git存储库的路径。如果使用Gusy::VERSION进行交互式探索,我可以成功查看Gusy命名空间(例如打印应用版本bundle console)。

如何访问映射的模型以及在何处以及如何配置数据库连接? 我在Padrino::Gusy::模块中看不到任何相关内容。

irb会话可能如下所示:

require 'gusy'
Gusy::Seminar.create(:name => 'from gusy_fill' # => NameError: uninitialized constant Gusy::Seminar

我想实现这个而不用创建第二个装载Gusy的Padrino应用程序(为此,指针包含在生成的gusy / README.md中)。

作为最初的状态,我会遇到同样的问题,如果我会在同一个应用程序中做我想做的事情:在gusy/bin中编写一个与数据库对话的小脚本,真的在设置中就像当致电padrino console

1 个答案:

答案 0 :(得分:2)

很抱歉听到您遇到此问题。你把它提起来是件好事,因为我现在试图把我的想法放在这个主题上一段时间,这让我陷入其中:)。我为你准备了repo,解释了如何用我们现在在Padrino中所做的事情来做。

自述文件(我后来粘贴)解释了其背后的原因,并提出了一些问题,让我们思考我们实施它们的方式。我很想听听你的想法:)。

Padrino中的Gemified应用程序

这个回购打算回答 How to access Padrino model and database in a “standalon” (bin/) script?How to access a gemified Padrino Apps Model from other gem that requires that App

问题

简而言之,有两个类似性质的问题,都与gemified app中定义的模型有关:

  • 他们需要从其他宝石/项目访问;
  • 他们需要从宝石应用程序bin访问,除了启动之外还要做其他事情 Padrino服务器。

示例

首先是gemified-app。这是一个宝石化的Padrino应用程序。它还包含一个模型 名为SomeModel,名为property。{/ p>

然后有access-gemified-app-without-padrino;一个ruby脚本,加载gemified应用程序 访问模型。

最后,another-app这是一个普通的Padrino应用,只需加载gemified-app即可使用 它的模型(SomeModel)。

当前Padrino设置的问题

使用padrino g project gemified-app --orm sequel --gem --tiny创建应用会为您提供 关注gemspec

# -*- encoding: utf-8 -*-
require File.expand_path('../lib/gemified-app/version', __FILE__)

Gem::Specification.new do |gem|
  gem.authors       = ["Darío Javier Cravero"]
  gem.email         = ["dario@uxtemple.com"]
  gem.description   = %q{Padrino gemified app example}
  gem.summary       = %q{Padrino gemified app example}
  gem.homepage      = ""

  gem.files         = `git ls-files`.split($\)
  gem.executables   = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
  gem.test_files    = gem.files.grep(%r{^(test|spec|features)/})
  gem.name          = "gemified-app"
  gem.require_paths = ["lib", "app"]
  gem.version       = GemifiedApp::VERSION

  gem.add_dependency 'padrino-core'
end

关键点是gem.require_paths = ["lib", "app"]gem.add_dependency 'padrino-core'

gem.require_paths = ["lib", "app"]解释了为什么models/some_model.rb在我们无法使用时无法使用 将宝石加载到其他地方。它很简单,没有添加到$LOAD_PATH :(。

gem.add_dependency 'padrino-core'暗示我们以后可能会遗漏某些内容。怎么了 与ORM或渲染器等依赖关系?我们应该加载吗?我认为这是一个问题 你希望实现的目标,但我说大多数时候都是。

我们的宝石应用程序依赖项仍然列在我们的Gemfile中,它们只会添加到。{1}}中 当前范围,而不是任何需要我们gemified-app宝石的宝石。

首次尝试解决此问题

为此,我们应该做两件事:

'models'添加到gem.require_paths = ["lib", "app"],使其成为: gem.require_paths = ["lib", "app", "models"]。 这将确保gemified-app/models目录中的任何内容都包含在您的目录中 宝石。

为了便于对此进行测试,我们会在bundler中使用access-gemified-app-without-padrino 测试脚本我们将添加如下所示的Gemfile

source 'https://rubygems.org'

gem 'gemified-app', path: '../gemified-app'
gem 'pry'

现在,在您的新应用中,转到REPL bundle exec pry并尝试require 'gemified-app'。 然后尝试SomeModel.all。它会失败。为什么?因为你没有require 'some_model'

如果你这样做,它仍然无效。为什么?因为没有模型的依赖, 即加载了sequelsqlite3(不是直接依赖,而是通过连接)。

您有两种选择:在Gemfile上手动加载它们,或者将它们定义为 对gemified-app.gemspec的依赖。 我认为后者是一个更好的选择,因为你已经包括了这个模型而且你已经 期待它的依赖性随之而来。它希望如此:

# gemified-app/gemified-app.gemspec

  # ...

  gem.add_dependency 'padrino-core'
  gem.add_dependency 'padrino-helpers'
  gem.add_dependency 'slim'
  gem.add_dependency 'sqlite3'
  gem.add_dependency 'sequel'
  gem.add_development_dependency 'rake'

  # ...


# gemified-app/Gemfile
source 'https://rubygems.org'

# Distribute your app as a gem
gemspec

您必须明确包含您需要的所有宝石。这可能看起来很麻烦,但在 公平,它让您更好地了解您的应用程序需要什么。最终你会 意识到你甚至不需要捆绑器和Gemfile:)。

好的,所以,继续启动您的REPL并输入require 'gemified-app'require 'some_model'。 然后尝试SomeModel.all。而且......它会失败:(。为什么?因为Sequel::Base没有被定义。现在你可能想知道: 我放入sequel的{​​{1}}引用发生了什么变化?嗯,它就是这样的: 参考,它不会为你需要宝石。 这不会发生在Padrino身上,因为我们正在使用

gemified-app.gemspec

在我们的require 'rubygems' unless defined?(Gem) require 'bundler/setup' Bundler.require(:default, RACK_ENV) 中,只在我们config/boot.rb上加载所需的宝石。

所以问题是......我们应该手动加载吗?如果是这样,在哪里?

嗯,因为这本身就是一颗宝石,我相信最好的地方就是Gemfile。 加载所需的所有宝石将使该文件看起来像:

lib/gemified-app.rb

好吧,所以我们都设置好了......回到REPL,做你的要求

require 'padrino-core'
require 'padrino-helpers'
require 'slim'
require 'sqlite3'
require 'sequel'

module GemifiedApp
  extend Padrino::Module
  gem! "gemified-app"
end

并尝试require 'gemified-app' require 'some_model' 。并且......它会失败:(。再次!:/为什么?因为没有与之相关的 数据库。 Padrino正在通过SomeModel.all为我们加载。

另一个问题出现了......我们是否应该在宝石中加入config/database.rb? 我看待它的方式,我们不应该。我看到它的方式,数据库连接是每个应用程序的东西 应该在本地定义,因为它可能包含访问它的特定凭据或类似的东西。 我们的示例config/database.rb脚本将如下所示:

access-gemified-app-without-padrino/do-somethin.rb

是的,连接代码与我们的Padrino应用程序几乎相同,我们正在重复使用其数据库 对于这个例子。

那是一些骑行:)但我们终于成功了。请参阅回购中的示例应用程序以进行一些工作 实例

需要require 'gemified-app' Sequel::Model.plugin(:schema) Sequel::Model.raise_on_save_failure = false # Do not throw exceptions on failure Sequel::Model.db = Sequel.connect("sqlite:///" + File.expand_path('../../gemified-app/db/gemified_app_development.db', __FILE__), :loggers => [logger]) require 'some_model' SomeModel.all.each do |model| puts %Q[#{model.id}: #{model.property}] end :/

我不认识你,但我根本不喜欢你。必须做那样的事情意味着我 真的要挑选我的模特&#39;非常谨慎地命名,不要与我可能想要使用的任何东西发生冲突 在将来。 我认为模块是它的答案,但这是当前的事态。见 关于此的更多结论。

另一种方法

将您的模型图层分隔为自己的宝石,并从您的(宝石化或非宝石版)Padrino应用程序中获取它。 这可能是最干净的,因为您可以隔离模型的测试甚至创建 针对不同情况的不同模型,可能会也可能不会使用下面的相同数据库。

它还可以封装所有连接细节。

结论

我认为我们应该审查Padrino的宝石应用程序方法。

我们应该使用gemspec而不是Gemfile来实现硬依赖吗?

我们是否应该对模型进行命名(我知道过去我们遇到过一些问题)?

我们是否应该教导用户在他们的宝石中明确要求或检查家属和 需要他们吗?

我们是否应该教我们的用户如何加载他们的依赖关系并对它更加负责?在末尾 当天,如果他们走上了宝石化的应用程序路线,他们显然更熟练的Ruby和 应该知道这种东西。

思考? :)