ruby 如何从外部访问gemified Padrino应用程序模型(不在控制器中,但例如独立脚本)

j9per5c4  于 2023-08-04  发布在  Ruby
关注(0)|答案(1)|浏览(79)

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

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

字符串
我想从第二个gem或bin/中的脚本访问此模型。
现在,例如,我需要来自第二个gem“gusy_fill”的Gusy。Gemfile用于设置Gusy Git仓库的路径。我可以成功地看到Gusy命名空间(例如打印应用程序版本Gusy::VERSION)(如果使用bundle console进行交互式浏览)。
如何访问Map的模型,在哪里以及如何配置数据库连接?我在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时的设置中。

6yt4nkrj

6yt4nkrj1#

很抱歉听到你遇到麻烦了。你提出来很好,因为我一直试图把我的想法围绕这个主题一段时间了,这把我推了进去:)。我已经为你准备了一个repo,解释如何用我们现在在帕德里诺的东西来做。
README(我稍后会粘贴)解释了它背后的原因,并提出了一些问题,让我们思考我们实现它们的方式。我很想听听你的想法:)。

Padrino中的Gemified应用

这个repo打算回答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应用程序中定义的模型相关:

  • 它们需要从另一个gems/项目访问;
  • 它们需要从gemified应用程序的bin访问,执行除启动Padrino服务器之外的其他操作。

示例

首先是gemified-app。这是一个Padrino应用程序,是gemified。它还包含一个名为SomeModel的模型,其中有一个名为property的字段。
然后是access-gemified-app-without-padrino加载gemified应用程序以访问模型的ruby脚本。
最后,还有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"]解释了为什么当我们在其他地方加载gem时models/some_model.rb不可用。简单的说就是没有添加到$LOAD_PATH:(。
gem.add_dependency 'padrino-core'提示我们稍后可能会缺少一些东西。ORM或渲染器等依赖项会发生什么?我们要把这些装上吗?我认为这是一个你想要实现的问题,但我会说,大多数时候是的。
我们的gemified应用依赖项仍然列在我们的Gemfile中,它只会被添加到当前范围中,而不会被添加到任何需要我们的gemified-app gem的gem中。

第一次尝试解决此问题

要做到这一点,我们应该做两件事:
'models'添加到gem.require_paths = ["lib", "app"],使其变为:gem.require_paths = ["lib", "app", "models"] .这将确保gemified-app/models目录中的任何内容都包含在gem中。
为了更容易测试,我们将使用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。它会失败的。为什么?因为你没有。
但如果你这样做,它仍然不会起作用。为什么?因为模型的依赖性,即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


你必须明确地包括你需要的所有gem。这可能看起来很麻烦,但公平地说,它让你更好地了解你的应用程序需要什么。最终你会发现你甚至不需要bundler和Gemfile:)。
好的,那么,继续启动您的REPL并输入require 'gemified-app'require 'some_model'。然后尝试SomeModel.all。而且...它会失败的:(。为什么?因为Sequel::Base没有定义。现在你可能想知道:我放在gemified-app.gemspec中的sequel的引用发生了什么?嗯,只是:一个参考,它不会需要你的宝石。这也不会发生在帕德里诺身上,因为我们使用的是

require 'rubygems' unless defined?(Gem)
require 'bundler/setup'
Bundler.require(:default, RACK_ENV)


在我们的config/boot.rb和只加载所需的宝石在我们的Gemfile
所以问题是...我们要手动装载吗?如果有,在哪里?
好吧,既然这本身就是一块宝石,我相信最好的地方是在lib/gemified-app.rb。加载所需的所有gems将使此文件看起来像:

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

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


好的,我们都准备好了……回到REPL,执行您的要求

require 'gemified-app'
require 'some_model'


试试SomeModel.all而且...它会失败的:(。又来了!为什么?因为和数据库没有联系。Padrino通过config/database.rb为我们加载了这个。
另一个问题出现了…我们是否也应该在gem中包含config/database.rb?在我看来,我们不应该。在我看来,数据库连接是每个应用程序都应该在本地定义的东西,因为它可能包含访问它的特定凭据或类似的东西。我们的示例access-gemified-app-without-padrino/do-somethin.rb脚本将如下所示:

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


是的,连接代码与我们的Padrino应用程序几乎相同,我们在这个例子中重用了它的数据库。
这是一个很好的旅程:)但我们终于做到了。请参阅代码库中的示例应用程序以获取一些工作示例。

require some_model:/

我不认识你,但我一点也不喜欢。不得不做这样的事情意味着我真的必须非常小心地选择我的模特的名字,不要与我将来可能想要使用的任何东西相冲突。我认为模块是解决这个问题的答案,但这是目前的状况。关于这方面的更多信息,请参见结论。

另一种方法

将你的模型层分离到它自己的gem中,并从你的(gemified与否)Padrino应用程序中要求它。这可能是最干净的,因为您可以隔离模型的测试,甚至可以为不同的情况创建不同的模型,这些情况可能会使用相同的数据库,也可能不会使用相同的数据库。
它还可以封装所有连接细节。
结论
我认为我们应该回顾一下Padrino对gemified应用程序的方法。
我们应该使用gemspec而不是Gemfile来处理硬依赖吗?
我们是否应该命名模型(我知道我们过去在这方面有一些问题)?
我们应该教用户在他们的gem中做显式的requires,还是检查dependecies并要求他们?
我们是否应该教我们的用户如何加载他们的依赖项,并对此承担更多责任?在一天结束的时候,如果他们走gemified应用程序路线,他们显然更精通Ruby,应该意识到这类东西。
Thoughts?:)

相关问题