`
like.eagle
  • 浏览: 248657 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

MongoDB+Rails的Demo实现

阅读更多

参考博文:http://hlee.iteye.com/blog/605762

1,安装需要的gem包
gem install mongo_mapper

如果顺利的话,可以安装成功,并且包依赖的其他gem也都同时安装上了 , 例如在美国的linode服务器上,直接执行上面命令,就安装成功了,但是在大陆无法安装成功,至少我是经过下面的安装步骤成功的:

gem install jnunemaker-validatable --source http://gemcutter.org
gem install bson --source http://rubygems.org
gem install mongo --source http://gemcutter.org
gem install plucky --source http://gemcutter.org
gem install mongo_mapper --source http://gemcutter.org

2,新建rails工程

  cd D:\QP\workspace
  rails todo

 

3,修改你的enviroment.rb,加上下面的话。

Rails::Initializer.run do |config|

    ...

    config.gem "mongo_mapper"   #追加

    ...

end

 

4,演示的本应用是一个todo list,实现类似备忘或者待完成任务列表的功能。

项目中有一个Project的model和一个Tasks的model,他们有has_many的关系,为了简化开发,突出mongoDB的使用,将使用Ryan Bates的Nifty Generators插件来实现。当然,不使用这个插件,我们的项目应用完全可以正常演示。
安装Nifty Generators插件:gem install nifty-generators

首先,我们需要通过如下语句创建项目的layout。
在应用项目D:\QP\workspace\todo目录下执行:ruby script/generate nifty_layout

 

出现的错误以及解决办法:

============================================================

出现如下的错误:
D:\QP\workspace\todo>ruby script/generate nifty_layout
c:/ruby/lib/ruby/gems/1.8/gems/bson-1.1.5/lib/bson.rb:50: undefined method
`bytes' for "\001\000\000\000":String (NoMethodError)
        from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:i
n `gem_original_require'
        from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:i
n `require'
        from c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.3.8/lib/active_
support/dependencies.rb:156:in `require'
        from c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.3.8/lib/active_
support/dependencies.rb:521:in `new_constants_in'
        from c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.3.8/lib/active_
support/dependencies.rb:156:in `require'
        from c:/ruby/lib/ruby/gems/1.8/gems/mongo-1.1.5/lib/mongo.rb:38
        from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:i
n `gem_original_require'
        from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:i
n `require'
         ... 25 levels...
        from c:/ruby/lib/ruby/gems/1.8/gems/rails-2.3.8/lib/commands/genera
te.rb:1
        from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:i
n `gem_original_require'
        from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:i
n `require'
        from script/generate:3
经过调查,可能问题是本地的ruby版本(1.8.6)太低,要1.8.7以上才不会出现错误↓。
https://github.com/mongodb/mongo-ruby-driver/pull/9

升级ruby版本后是否能解决该错误还没试,预计明天测试。

 

升级ruby1.8.6至1.8.7,继续执行ruby script/generate nifty_layout命令,出现如下的信息:

D:\QP\workspace\todo>ruby script/generate nifty_layout
**Notice: C extension not loaded. This is required for optimum MongoDB Ruby
 driver performance.
  You can install the extension as follows:
  gem install bson_ext

  If you continue to receive this message after installing, make sure that
the
  bson_ext gem is in your load path and that the bson_ext and mongo gems ar
e of the same version.

      exists  app/views/layouts
      exists  public/stylesheets
      exists  app/helpers
      create  app/views/layouts/application.html.erb
      create  public/stylesheets/application.css
      create  app/helpers/layout_helper.rb

根据上述提示信息,执行 gem install bson_ext

 

继续出现错误提示信息:
D:\QP\workspace\todo>gem install bson_ext
ERROR:  Error installing bson_ext:
        The 'bson_ext' native gem requires installed build tools.

Please update your PATH to include build tools or download the DevKit
from 'http://rubyinstaller.org/downloads' and follow the instructions
at 'http://github.com/oneclick/rubyinstaller/wiki/Development-Kit'

如何解决?且看如下的步骤
打开http://rubyinstaller.org/downloads网页;
下载DevKit(Development Kit):http://github.com/downloads/oneclick/rubyinstaller/DevKit-tdm-32-4.5.1-20101214-1400-sfx.exe
DevKit安装:见手顺http://github.com/oneclick/rubyinstaller/wiki/Development-Kit

 

DevKit安装步骤
Installation Detail – Step 1
If you previously installed the legacy DevKit devkit-3.4.5r3-20091110.7z, its artifacts were extracted into each Ruby installation and need to be manually removed. Remove the gcc.bat, make.bat, and sh.bat stub batch files in <RUBY_INSTALL_DIR>\bin and the<RUBY_INSTALL_DIR>\devkit subdirectory for each Ruby installation using the legacy DevKit.
If you previously installed one of the legacy self-extracting DevKit’s, follow the SFX DevKit upgrade instructions.
Installation Detail – Step 2
The current DevKit is available at the RubyInstaller download page with older versions available at the archives page. As backup, check ourGitHub downloads page.
如果以前本机没有装过DevKit ,Step1和Step2可以忽略。
Installation Detail – Step 3
Left double-click the self-extracting executable downloaded from Step 2 and choose a directory (without spaces) to install the DevKit artifacts into. For example, C:\DevKit. In the instructions that follow, the directory that you selected is identified as <DEVKIT_INSTALL_DIR>.
解压缩DevKit安装包至DEVKIT_INSTALL_DIR( C:\DevKit)目录中。
     
Installation Detail – Step 4
cd <DEVKIT_INSTALL_DIR> from Step 3 above.
ruby dk.rb init to generate the config.yml file to be used later in this Step. Your installed Rubies will be listed there (only those installed by a RubyInstaller package are detected at present).
本步骤安装成功时在CMD控制台出现的信息:
C:\DevKit>ruby dk.rb init
        [INFO] found RubyInstaller v1.8.7 at C:/Ruby187
        Initialization complete! Please review and modify the auto-generated
        'config.yml' file to ensure it contains the root directories to all
        of the installed Rubies you want enhanced by the DevKit.

edit the generated config.yml file to include installed Rubies not automagically discovered or remove Rubies you do not want to use the DevKit with.
在刚刚生成的config.yml中添加该文件中没有检测到的ruby程序。(因为也许你的机器中安装了不止一个版本的ruby。)
[optional] ruby dk.rb review to review the list of Rubies to be enhanced to use the DevKit and verify the changes you made to it are correct.
本步骤安装成功时在CMD控制台出现的信息:
        C:\DevKit>rubydk.rb review
        Based upon the settings in the 'config.yml' file generated
        from running 'ruby dk.rb init' and any of your customizations,
        DevKit functionality will be injected into the following Rubies
        when you run 'ruby dk.rb install'.

        C:/Ruby187
        C:/ruby

finally, ruby dk.rb install to DevKit enhance your installed Rubies. This step installs (or updates) an operating_system.rb file into the relevant directory needed to implement a RubyGems pre_install hook and a devkit.rb helper library file into<RUBY_INSTALL_DIR>\lib\ruby\site_ruby. NOTE: you may need to use the --force option to update (with backup of the originals) the above mentioned files as discussed at the SFX DevKit upgrade FAQ entry.
本步骤安装成功时在CMD控制台出现的信息:
        C:\DevKit>ruby dk.rb install
        [INFO] Updating convenience notice gem override for 'C:/Ruby187'
        [INFO] Installing 'C:/Ruby187/lib/ruby/site_ruby/devkit.rb'
        [INFO] Installing 'C:/ruby/lib/ruby/site_ruby/1.8/rubygems/defaults/operating_system.rb'
        [INFO] Installing 'C:/ruby/lib/ruby/site_ruby/devkit.rb'

Installation Detail – Step 5
Confirm your Ruby environment is correctly using the DevKit by running gem install rdiscount --platform=ruby. RDiscount should install correctly and you should see Temporarily enhancing PATH to include DevKit... in the screen messages. Next run ruby -rubygems -e "require 'rdiscount'; puts RDiscount.new('**Hello RubyInstaller**').to_html" to confirm that the rdiscount gem is working.
本步骤安装成功时在CMD控制台出现的信息:
        C:\DevKit>gem install rdiscount --platform=ruby
        Temporarily enhancing PATH to include DevKit...
        Building native extensions.  This could take a while...
        Successfully installed rdiscount-1.6.5
        1 gem installed
        Installing ri documentation for rdiscount-1.6.5...
        Installing RDoc documentation for rdiscount-1.6.5...

        C:\DevKit>ruby -rubygems -e "require 'rdiscount'; puts RDiscount.new('**Hello RubyInstaller**').to_html"
        <p><strong>Hello RubyInstaller</strong></p>

安装完DevKit之后,继续执行gem install bson_ext,安装成功,CMD控制台出现如下的信息:
C:\DevKit>gem install bson_ext
Temporarily enhancing PATH to include DevKit...
Building native extensions.  This could take a while...
Successfully installed bson_ext-1.1.5
1 gem installed
Installing ri documentation for bson_ext-1.1.5...

No definition for method_serialize

No definition for method_deserialize
Installing RDoc documentation for bson_ext-1.1.5...

No definition for method_serialize

No definition for method_deserialize

回到rails工程目录(D:\QP\workspace\todo)继续执行命令:ruby script/generate nifty_layout

 

命令执行成功,下面是控制台信息:
D:\QP\workspace\todo>ruby script/generate nifty_layout
      exists  app/views/layouts
      exists  public/stylesheets
      exists  app/helpers
   identical  app/views/layouts/application.html.erb
   identical  public/stylesheets/application.css
   identical  app/helpers/layout_helper.rb
============================================================

 

5,然后,我们将通过generate和nifty的scaffold创建project的model,这个表只有一个字段叫name,而且,使用mongoDB(没有sechma的概念,也就是不需要一个migrate来控制版本),我们需要这个加上参数--skip-migration来创建如下:
ruby script/generate nifty_scaffold project name:string --skip-migration

   #创建model成功后,在mongoDB中自动创建了一个todo-development的数据库,
   #并且在该数据库下创建了project model对应的prjects collection,且prjects中含有key name。
  (类似于以前mysql中model和table的对应关系)
   #(原因参照我们在/config/initializers/mongo_config.rb里的配置)

当然,上面的脚手架会创建modelcontroller和view,然而,默认创建的是ActiveRecord的基于schema的model,如下:
#/app/models/project.rb  
class Project < ActiveRecord::Base    
  attr_accessible :name    
end   

以上代码又脚手架生成。那么,我们需要把ActiveRecord的model,改成MongoMapper的类型,也就是把继承关系从ActiveRecord::Base变成MongoMapper::Document。我们使用key这个方法标明该MongoMapper的字段属性。我们的属性是name,再加上这个字段的类型String,那么定义如下:
Ruby代码
#/app/models/project.rb  
class Project    
  include MongoMapper::Document    
  key :name, String    
end   

通过以上的修改,我们就已经拥有了所有添加,更新,删除和列表的操作,这就和我们之前用脚手架创建,用ActiveRecord加上关系型数据库一样,只是我们使用的是MongoMapper和MongoDB。

 

====================================================

启动rails项目todo的服务器,启动过程中的出错以及解决办法:
4340
=> Booting Mongrel
=> Rails 2.3.8 application starting on http://0.0.0.0:3000
c:/ruby/lib/ruby/gems/1.8/gems/bson-1.1.5/lib/bson.rb:50: undefined method `bytes' for "\001\000\000\000":String (NoMethodError)
 from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
 from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
 from c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:156:in `require'
 from c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:521:in `new_constants_in'
 from c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:156:in `require'
 from c:/ruby/lib/ruby/gems/1.8/gems/mongo-1.1.5/lib/mongo.rb:38
 from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
 from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
  ... 30 levels...
 from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
 from ./script/server:3
 from -e:4:in `load'
 from -e:4
解决办法:更改eclipse中关于rails和ruby指向的配置。

 

 

启动服务器后,在浏览器输入URL→http://localhost:3000/projects后,出现的错误以及解决办法:

 

解决办法:去http://www.sqlite.org/download.html网站下载sqlite3.dll文件,放置C:\Windows\System32目录下即可;

 

再次输入http://localhost:3000/projects,项目打开成功。

====================================================

 

6,创建一个数据对象(即文档,相当于mysql里某个表里的一条数据):
点击 New Project link可以创建project的子文档,此处我们创建了一个名为Yardwork的数据对象(文档,元素之类的叫法都可以)。
访问mongoDB,可发现数据库现有的状态为:
D:\QP\NoSQL\mongodb-win32-i386-1.6.5\bin>mongo.exe
MongoDB shell version: 1.6.5
connecting to: test
> show dbs
admin
local
mydb
todo-development
> use todo-development
switched to db todo-development
> db.projects.find()
{ "_id" : ObjectId("4d2c00c258f4f30474000002"), "name" : "Yardwork" }

 

7,添加更多的属性:
由于MongoDB没有schema-less(数据版本记录)我们可以非常容易的添加和更改model的属性,而不需要执行任何migrations的操作。比如,我们需要添加一个priority的属性,我们仅仅需要的是修改Project model如下:
Ruby代码
#/app/models/project.rb
 class Project 
   include MongoMapper::Document 
  
   key :name, String, :required => true 
   key :priority, Integer 
 end 

 

如同ActiveRecord支持的一样,model的属性可以直接和form元素中关联。例如,新定义的priority可以显示在下拉菜单中如下:
Ruby代码
#/app/views/projects/_form.html.erb  
    <% form_for @project do |f| %>    
      <%= f.error_messages %>    
      <p>    
        <%= f.label :name %><br />    
        <%= f.text_field :name %>    
      </p>    
      <p>    
        <%= f.label :priority %><br />    
        <%= f.select :priority, [1,2,3,4,5] %>    
      </p>    
      <p><%= f.submit "Submit" %></p>    
    <% end %>   

 

在显示页面我们也需要做相应的修改以便显示该字段:
Ruby代码
#/app/views/projects/show.html.erb  
    <% title "Project" %>    
    <p>    
      <strong>Name:</strong>    
      <%=h @project.name %>    
    </p>    
    <p>    
      <strong>Priority:</strong>    
      <%=h @project.priority %>    
    </p>    
    <p>    
      <%= link_to "Edit", edit_project_path(@project) %> |    
      <%= link_to "Destroy", @project, :confirm => 'Are you sure?', :method => :delete %> |    
      <%= link_to "View All", projects_path %>    
    </p>   

 

这样,我们在创建新Project的时候,我们将会看到一个提示选择priority的下拉菜单,并且,提交创建以后,会得到一个包括priority显示的Project信息显示页面。

当然,如果我们在添加priority属性前创建了Project。那么,添加priority属性后,这个先前创建的project的priority是什么呢?我们可以通过访问看到是空白,也就是说,如果一条记录没有属性,那么对于MongoDB的文件类型存储引擎会默认是nil。

 

访问mongoDB,可发现数据库现有的状态为:
D:\QP\NoSQL\mongodb-win32-i386-1.6.5\bin>mongo.exe
MongoDB shell version: 1.6.5
connecting to: test
> show dbs
admin
local
mydb
todo-development
> use todo-development
switched to db todo-development
> db.projects.find()
{ "_id" : ObjectId("4d2eb53a58f4f30230000002"), "name" : "housework", "prio
rity" : 2 }
{ "_id" : ObjectId("4d2c00c258f4f30474000002"), "name" : "Yardwork"}

 

8,处理表之间的关联:

在我们前文所述的Todo项目需求描述中,我们还需要定义一个Task model,每个Project会对应多个Task。同样,我们将和创建Project一样,使用脚手架创建这个model。值得注意的是,project_id之前在ActiveRecord都是integer类型,在这里我们使用字符串类型的如下(晓夜:原因见下文):
Ruby代码
script/generate nifty_scaffold task project_id:string name:string completed:boolean --skip-migration 

如同,修改Project model一样,我们同样修改Task model指定继承MongoMapper如下:
Ruby代码
#/app/models/Task.rb
 class Task 
   include MongoMapper::Document 
    
   key :project_id, ObjectId 
   key :name, String 
   key :completed, Boolean 
    
   belongs_to :project 
 end 

再一次如同Project model,我们将按照MongoMapper的语法修改字段定义。可能我们已经习惯吧poject_id定义成整型,然而,对于MongoDB这里稍微有点区别,我们需要ObjectId类型来存储所有id。
至于,处理不同表之前的关联,我们可以像ActiveRecord一样定义belongs_to,当然,稍微有点不同,在Project中我们需要定义has_many :tasks,在MongoMapper中需要用many代替如下:
Ruby代码
#/app/models/project.rb  
    class Project    
      include MongoMapper::Document    
        
      key :name, String, :required => true    
      key :priority, Integer    
          
      many :tasks    
    end   

现在,我们就可以使用我们用脚手架创建的model controller和view来创建Task了,比较有技巧的一点是,我们开始的时候定义了project_id的属性是String所以,在这里的view界面中就会创建一个对应的文本输入框。然后,我们需要修改这个form表单的,以便我们可以使用下拉菜单选择对应的所属Project。对于,下拉菜单显示project列表,我们完全可以参照ActiveRecord的方式,使用collection_select来实现,如下:
Ruby代码
#/app/views/tasks/_form.html.erb  
    <% form_for @task do |f| %>    
      <%= f.error_messages %>    
      <p>    
        <%= f.label :project_id %><br />    
        <%= f.collection_select :project_id, Project.all, :id, :name %>    
      </p>    
      <!-- Rest of form... -->   

也就是,我们可以通过如下方式创建Task并选择所属Project。

当我们完成创建task后会默认跳到显示task的界面,我们会看到刚刚创建的所属project的id,当然我们更愿意看到Project的name。所以,需要用@task.project.name来代替@task.project_id如下:
Ruby代码
#/app/views/tasks/show.html.erb  
    <% title "Task" %>    
        
    <p>    
      <strong>Project:</strong>    
      <%=h @task.project.name %>    
    </p>    
    <!-- Rest of form -->   

这样,我们就可以使用ActiveRecord的模式来显示相关的model的信息了。(晓夜:也就是belong_to has_many的功能)

 

================================================================

MongoDB的查询

我们将通过对于一些在控制台查询Mongo model技巧的介绍来结束本文。实际上,MongoMapper的查询和ActiveRecord很像。例如,当我们要查询所有projects的时候用如下:

>> Project.all
=> [#<Project name: "Yardwork", _id: 4b39d8c9a175750357000001, priority: nil>, #<Project name: "Housework", _id: 4b39fbd1a175750357000002, priority: 3>]
We can also find a project by its id…


我们也可以通过id来查询:

>> Project.find('4b39d8c9a175750357000001')
=> #<Project name: "Yardwork", _id: 4b39d8c9a175750357000001, priority: nil>
…or supply options to all to find records in a given order.


查询并按照name降序输出:

>> Project.all(:order => "name DESC")
=> [#<Project name: "Yardwork", _id: 4b39d8c9a175750357000001, priority: nil>, #<Project name: "Housework", _id: 4b39fbd1a175750357000002, priority: 3>]



如同,在ActiveRecord中的通过conditions的查询一样,MongoMapper也支持条件的查询,更容易的是,可以直接输入条件,例如选择所有,priority为3的project:

>> Project.all(:priority => 3)
=> [#<Project name: "Housework", _id: 4b39fbd1a175750357000002, priority: 3>]



那么,对于条件更为复杂的查询,由于,Mongo不是基于SQL的关系型数据库,所以也不能够通过类似传入SQL语句的方式进行查询。不过,MongoDB也有一套自己的查询方式,MongoMapper良好而简洁的支持这种查询。例如,要查询projects中所有,priority大于或等于2的记录:

>> Project.all(:priority.gte => 2)
=> [#<Project name: "Housework", _id: 4b39fbd1a175750357000002, priority: 3>]



我们也可以通过以数组为参数的查询,例如,查询所有priority是2或者3的projects:

>> Project.all(:priority.in => [2,3])
=> [#<Project name: "Housework", _id: 4b39fbd1a175750357000002, priority: 3>]



当前而言,关于MongoMapper的复杂查询相关的文档还比较少,如果,你需要更多更为详尽的资料,那么你应该去阅读github上MongoMapper的测试文档

本文的介绍将到此为止,我们仅仅演示了MongoDB和MongoMapper的最基本的使用情况。如果,需要真正的使用,你应该进一步的深入了解相关的知识。你可以加入这个邮件列表,或者follow MongoDB的Twitter

现在已经是时候,问问自己到底选择MongoDB还是传统的关系型数据库了。当然,是不是采用MongoDB和MongoMapper的决定权在于你自己,然而,MongoDB却真的值得一试,这样你才能更知道他的好处。从更长远的角度来说,文件型引擎的数据库在Rails的应用上将会扮演越来越显著的角色。

================================================================

 

分享到:
评论
1 楼 yinmang1215 2011-01-25  
不错,感觉这些方面的文档真是稀缺,谢了。

相关推荐

Global site tag (gtag.js) - Google Analytics