如何从外部访问gemified Padrino Apps模型(不在控制器中,但是例如独立脚本) [英] How to access a gemified Padrino Apps Model from outside (not in controller, but e.g. a standalone script)

查看:123
本文介绍了如何从外部访问gemified Padrino Apps模型(不在控制器中,但是例如独立脚本)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 #gusy / models / seminar.rb 
班研讨会< Sequel :: Model
#希望在这里定义的不相关的东西
结束

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



现在,例如我需要从第二个宝石gusy_fillGusy。 Gemfile可以设置一个Gusy git仓库的路径。如果使用 bundle console Gusy :: VERSION ) >。



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



一个irb会话可能如下所示:

  require'gusy'
Gusy :: Seminar.create(:name =>'from gusy_fill'#=> NameError:未初始化的常量Gusy :: Seminar

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



正如最初说的那样,如果我在同一个应用程序中做我想做的事情,我会遇到同样的问题:在 gusy / bin 与数据库进行交谈,真的是在调用 padrino console 时的设置。

解决方案

很抱歉听到你有这方面的问题,但你提出来很好,因为我一直在想这个问题,让我进入它:)。我准备了回购,以解释如何用我们现在拥有的产品在Padrino中。

自述文件(我之后正在粘贴)解释了它背后的原因,并提出了一些问题,以考虑我们实施的方式他们。我很想听听你的想法:)。



Padrino中的Gemified应用程序



本回购计划回答
如何在standalon(bin /)脚本中访问Padrino模型和数据库?
如何从需要该应用程序的其他gem访问gemified Padrino Apps模型



问题



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




  • 需要从其他gems / projects访问它们;
  • 需要从gemified应用程序 bin ,除了启动
    Padrino服务器以外的其他工作。



< h2>练习mple

首先是 gemified-app 。这是一个gemified的Padrino应用程序。它还包含一个名为 SomeModel 的模型
,其中一个字段名为 property



然后是 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íoJavier Cravero]
gem.email = [dario@uxtemple.com]
gem.description =%q {Padrino gemified app example}
gem.summary =%q {Padrino gemified应用示例}
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 在我们$ b $时不可用b在其他地方加载宝石。它简单的不添加到 $ LOAD_PATH :(。



gem.add_dependency' padrino-core'提示我们稍后可能会丢失一些东西。
和ORM或渲染器之类的依赖关系会发生什么情况?我们应该加载这些吗?我认为这是一个问题$ b $我们会说大部分时间都是。


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



第一次尝试解决这个问题



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

'models'添加到 gem.require_paths = [lib,app] 所以它变成:
gem.require_paths = [lib,app,models]
这将确保任何东西在
gem中包含 gemified-app / models 目录中的内容。



为了使测试更容易,我们将使用 bundler 并在我们的 access-gemified -app-without-padrino
测试脚本,我们将添加一个 Gemfile ,如下所示:

  source'https://rubygems.org'

gem'gemified-app',路径:'../gemified- app'
gem'pry'

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



如果你这样做,它仍然不起作用。为什么?因为没有模型的依赖关系,
ie sequel sqlite3 (不是直接依赖关系,而是通过

这里有两个选择:你在 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'

#将您的应用程序分配为宝石
gemspec

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

好吧,继续启动你的REPL并输入 require'gemified-app' require'some_model'
然后尝试 SomeModel.all 。并且...它会失败:(为什么?因为 Sequel :: Base 没有定义,现在你可能会想:
参考到续集我把我的 gemified-app.gemspec ?好吧,它只是:
a reference和
这对Padrino来说不会发生,因为我们正在使用

 需要'bundler / setup'
Bundler.require(:default,RACK_ENV)
需要'rubygems'在我们的 config / boot.rb 中,并且只在我们的 Gemfile 中加载所需的gem。 $ b



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



好吧,既然这是一个宝石本身,我相信最好的地方是在 lib / gemified-app.rb
加载所有需要的gems会使这个文件看起来像:

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

模块GemifiedApp
扩展Padrino :: Module
宝石! 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#不要在失败时抛出异常
Sequel :: Model.db = Sequel.connect(sqlite:///+ File.expand_path('../../ gemified-app / db / gemified_app_development .db',__FILE__),:loggers => [logger])

require'some_model'

SomeModel.all。每个do | model |
puts%Q [#{model.id}:#{model.property}]
end

是的,连接代码与我们的Padrino应用程序几乎相同,我们在此示例中重用了其数据库



这是一些骑:)但我们终于做到了。


$ b

需要 some_model :/ h3>

我不认识你,但我不喜欢那样。不得不这样做意味着我
真的必须非常仔细地选择我的模型名称,而不是与将来使用
的任何东西冲突。
我认为模块是答案,但这是目前的状况。请参阅
结论以了解更多信息。



另一种方法 将您的模型图层分离为它自己的宝石,并从你的(gemified或不)Padrino应用程序需要它。
这可能是最干净的,因为您可以为模型分离测试,甚至为不同的情况创建
的不同模型,这些情况可能会或可能不会使用下面的相同数据库。



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



结论



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



我们是否应该命名空间模型(我知道我们在过去有一些问题)?



我们应该教用户在他们的宝石中做明确的要求还是要检查依赖性和
是否需要它们?



我们应该教导用户如何加载它们的依赖关系,并更负责任吗?在一天的最后
,如果他们去了gemified的应用程序路线,他们显然比Ruby更精通,
应该知道这种东西。



想法? :)

I have a Padrino App called Gusy that specifies (Sequel) Models like

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

I want to access this Model from either a second gem, or a script in bin/.

Now, e.g. I require Gusy from a second gem "gusy_fill". The Gemfile is put up to set the path to a Gusy git repository. I can successfullly see the Gusy namespace (and e.g. print the apps Version Gusy::VERSION) if interactively exploring with bundle console.

How can I access the mapped models and where and how do I configure the database connection? I see nothing relevant in the Padrino:: or Gusy:: modules.

An irb session might look like this:

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

I want to achieve this without creating a second Padrino App that mounts Gusy (for that, pointers are included in the generated gusy/README.md).

As initially statet, I would have the very same issue, if I would do what I want from within the same app: write a small script in gusy/bin that talks with the database, really in the setting like when calling padrino console.

解决方案

Sorry to hear you're having trouble with this. It's good that you brought it up though because I've been trying to put my thoughts around the subject for a while now and this pushed me into it :). I've prepared a repo for you explaining how to do it with what we have now in Padrino.

The README (which I'm pasting afterwards), explains the reasoning behind it and puts some questions up for us to think about the way we've implemented them. I'd love to hear your thoughts about it :).

Gemified apps in Padrino

This repo intends to answer How to access Padrino model and database in a "standalon" (bin/) script? and How to access a gemified Padrino Apps Model from other gem that requires that App.

The issue

In short, there are two issues of the similar nature, both related to models defined in the gemified app:

  • they need to be accessed from another gems/projects;
  • they need to be accessed from the gemified app's bin, doing something else other than starting the Padrino server.

The example

First there's gemified-app. That's a Padrino app that is gemified. It also contains a model called SomeModel that has one field called property.

Then there's access-gemified-app-without-padrino; a ruby script that loads the gemified app to access the model.

Finally, there's another-app which is a regular Padrino app that just loads gemified-app to use its model (SomeModel).

Problems with the current Padrino setup

Creating an app with padrino g project gemified-app --orm sequel --gem --tiny will give you the following 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

The key points are gem.require_paths = ["lib", "app"] and gem.add_dependency 'padrino-core'.

gem.require_paths = ["lib", "app"] explains why models/some_model.rb isn't available when we load the gem somewhere else. It simple isn't added to $LOAD_PATH :(.

gem.add_dependency 'padrino-core' hints us that something might be missing later on. What happens with dependencies like the ORM or the renderer? Should we load those? I reckon that it's a matter of what you want to achieve but I'd say that most times yes.

Our gemified app dependencies are still listed in our Gemfile which will only be added in the current scope and not in any gems requiring our gemified-app gem.

A first attempt at solving this

For this to work there are two things we should do:

Add 'models' to gem.require_paths = ["lib", "app"] so that it becomes: gem.require_paths = ["lib", "app", "models"]. That will make sure that anything inside the gemified-app/models directory is included in your gem.

To make it easier to test this, we'll use bundler and in our access-gemified-app-without-padrino test script we'll add a Gemfile that looks like this:

source 'https://rubygems.org'

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

Now in your new app, go to the REPL bundle exec pry and try to require 'gemified-app'. Then try SomeModel.all. It will fail. Why? Because you didn't require 'some_model'.

It will still not work if you do that though. Why? Because none of the model's dependencies, i.e. sequel and sqlite3 (not a direct dependency but it is through the connection) are loaded.

Here you have two choices: you load them manually on your Gemfile or you define them as dependencies on gemified-app.gemspec. I regard the latter one as a better choice since you're already including the model and you're expecting its dependencies to come with it. It would like this:

# 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

You would have to explicitly include all the gems you will need. This may seem cumbersome but in all fairness it gives you a greater understanding of what your app needs. Eventually you will realise you don't even need bundler and the Gemfile :).

Alright, so, go ahead launch your REPL and type require 'gemified-app' and require 'some_model'. Then try SomeModel.all. And... It will fail :(. Why? Because Sequel::Base isn't defined. Now you might be wondering: what happened to the reference to sequel I put in my gemified-app.gemspec? Well, it's just that: a reference and it won't require the gem for you. This won't happen with Padrino either because we're using

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

in our config/boot.rb and that only loads required gems on our Gemfile.

So the question is... Should we load that manually? And if so, where?

Well, since this is a gem itself, I believe that the best place to do so would be in lib/gemified-app.rb. Loading all the gems needed will make this file look like:

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

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

Alright, so we're all set... Back to the REPL, do your requires

require 'gemified-app'
require 'some_model'

and try SomeModel.all. And... It will fail :(. Again! :/ Why? Because there's no connection to the database. Padrino was loading this for us through config/database.rb.

Another question arises... Should we include config/database.rb in the gem too? The way I see it, we shouldn't. The way I see it, the database connection is something every app should locally define as it may contain specific credentials to access it or stuff like that. Our sample, access-gemified-app-without-padrino/do-somethin.rb script will then look like this:

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

Yes, the connection code is pretty much the same than our Padrino app and we're reusing its database for this example.

That was some ride :) but we finally made it. See the sample apps in the repo for some working examples.

require some_model :/

I don't know you but I don't like that at all. Having to do something like that means that I really have to pick my models' names very carefully not to clash with anything I may want to use in the future. I reckon that modules are the answer to it but that's the current state of affairs. See the conclusion for more on this.

An alternative approach

Separate your model layer into its own gem and require it from your (gemified or not) Padrino app. This might probably be the cleanest as you can isolate tests for your models and even create different models for different situations that may or may not use the same database underneath.

It could also encapsulate all of the connection details.

Conclusion

I think we should review Padrino's approach to gemified apps.

Should we use the gemspec instead of the Gemfile for hard dependencies?

Should we namespace the models (I know we had some issues in the past with this)?

Should we teach users to do explicit requires in their gems or to inspect the dependecies and require them for them?

Should we teach our users how to load their dependencies and be more reponsible about it? At the end of the day, if they went the gemified app route they are clearly much more proficient in Ruby and should be aware of this kind of stuff.

Thoughts? :)

这篇关于如何从外部访问gemified Padrino Apps模型(不在控制器中,但是例如独立脚本)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆