Rails下cloud datastore的使用
?
?Rails下cloud datastore的使用
背景
部門有一個項目要用Ruby做 WebAPI,DB使用關系型數據庫Cloud Sql和非關系型數據庫Cloud Datastore 。
還不了解Ruby On Rails和CloudDatastore的請參考下面的鏈接。
http://guides.ruby-china.org/
https://thinkit.co.jp/story/2015/02/05/5594
?
1、 Windows下開發環境構建
1.1、構建Ruby On Rails 開發環境
① Ruby安裝
http://www.runoob.com/ruby/ruby-installation-windows.html
② Rail安裝
從CMD提示窗口輸入指令:gem install rails 開始安裝rails。
注意:在中國直接安裝會提示錯誤,因為gem默認的安裝源https://rubygems.org在國內不能訪問,還好先輩們早已為我們新搭建了一個gem安裝源http://gems.ruby-china.org。下面是切換安裝源的具體步驟,gem安裝源切換之后,Rails安裝就可以順利進行了。
第一步:刪除默認安裝源
CMD提示窗中輸入”gem sources -remove https://rubygems.org/”,回車換行。
第二步:添加新的安裝源
CMD提示窗輸入“gem sources -add? http://gems.ruby-china.org/”,回車換行。
③ ruby的IDE Ruby Mine安裝
http://qiita.com/HALU5071/items/6d6a39e44865d8d04de8
1.2、搭建開發用Cloud Datastore 數據庫
① 安裝Google Cloud SDK
http://www.apps-gcp.com/google-cloud-sdk-install
② 安裝cloud-datastore-emulator
CMD提示窗中輸入“gcloud components install cloud-datastore-emulator”,回車換行。
③ 創建一個開發用的DB:dev_datastore
CMD提示窗中輸入“cloud_datastore_emulator create db/dev_datastore”,回車換行。
前提條件:CMD當前運行目錄下要先創建一個db的文件夾。
創建好的DB文件結構圖如下:
?
2、 WEB API工程創建
在CMD提示窗中輸入“rails new my-first-api --api”,回車換行。
創建好的API工程結構見下圖,rails執行結果見附錄1。
| フォルダ構造 | ||
|
|
| 説明 |
| Gemfile |
| gemの依存関係を指定できるファイル |
| README.rdoc |
| 説明書 |
| Rakefile | ターミナルから実行可能なタスク |
|
| config.ru |
| Rackの設定 |
| app/ |
| アプリケーションを格納するディレクトリ |
| 主要なプログラムはこの配下に格納 | ||
|
| controllers/ | コントローラを格納するディレクトリ |
|
| controllers/application_controller.rb | アプリケーションで共通のコントローラ |
|
| models/ | モデルを格納するディレクトリ |
| config/ |
| プロジェクトの設定ファイルを格納するディレクトリ |
| config/environments/ |
| 環境単位の設定ファイルを格納するディレクトリ |
| config/initializers/ |
| 初期化ファイルを格納するディレクトリ |
| config/locales/ |
| 辭書ファイルを格納するディレクトリ |
| db/ |
| データベースの設定ファイルを格納するディレクトリ |
| doc/ |
| ドキュメントを格納するディレクトリ |
| lib/ |
| 複數のアプリケーション間で共有するライブラリを格納するディレクトリ |
|
| tasks/ | 自分で生成したRakefileを格納するディレクトリ |
| log/ |
| ログファイルが格納されるディレクトリ。ログファイルはアプリケーションと環境ごとに作成される |
| public/ |
| Web上に公開するファイルを格納するディレクトリ |
| tmp/ |
| キャッシュなど、一時的なファイルを格納されるディレクトリ |
| test/ |
| アプリケーションのテストに使うファイルを格納するディレクトリ |
| vendor/ |
| ライブラリや他のアプリケーションで共有するような外部ライブラリを格納するディレクトリ |
?
在附錄1的最后我們看到,bundle install 并沒有成功。原因是項目的gem安裝源出了問題。打開Gemfile把第一行的source 'https://rubygems.org'替換成source 'http://gems.ruby-china.org'。然后在CMD里重新執行bundle install命令,這樣項目需要的gem就能成功安裝。?
3、 新建cloud Datastore 的dataset對象
① Gem安裝
在Gemfile 中追加
gem 'google-cloud-datastore'
cmd里重新執行bundle install命令。
② 配置文件
在config/initializers/目錄下新建cloud_datastore.rb文件,文件內容如下圖所示。
從圖中我們可以看到cloud datastore 配置了三種環境下創建daset的參數,這些參數我們都放在了config/database.yml里。下面是database.yml里開發環境的配置信息,datastore 安裝的主機和datastore的數據庫名,這樣我們就可以使用datastore了。
development:????????host: 'localhost:8180'
????????project: ' dev_datastore '
?
?
cloud_datastore.rb
?
4、 非關系型數據庫 中ActiveModel 的使用
如果我們想要cloud datastore和關系型數據庫一樣,可以方便快捷使用model(ActiveModel),怎么辦?下面就是你想要的答案。
① 追加文件
在config/initializers/目錄下新建active_model_cloud_datastore.rb文件。
文件內容參照附錄2。
② model Class寫法
require_relative "../../config/initializers/active_model_cloud_datastore" class Customerinclude ActiveModelCloudDatastore
attr_accessor :customer_code, :customer_name
def attributes
%w(code customer_name)# 注意多個字段之間是半角空格來區分的
end end
這樣我們就可以使用activeModel很多功能,
#檢索所有數據@customers = Customer.all
#保存一條新的數據@customer = Customer.new
@customer.save
這里就不做詳細說明,具體參照附錄2里的各種方法。5、 spec測試
① 測試環境安裝
在Gemfile中追加下面的gem。
group :development, :test do# Test
gem 'rspec-rails', '~> 3.0'
gem 'rails-controller-testing'
end
然后在cmd工程目錄my-first-api下執行bundle install,安裝追加的gem。
② 測試環境初期化
在cmd工程目錄my-first-api下執行
bundle exec rails generate rspec:install
會生成下面的文件
create? .rspec create? spec create? spec/spec_helper.rb create? spec/rails_helper.rb③ controller測試
參考下面鏈接
http://qiita.com/shizuma/items/84e07e558abd6593df15
http://blog.naichilab.com/entry/2016/01/19/011514
④ model測試
參考下面的測試思路。
http://qiita.com/shizuma/items/c7b8d7b91e8f325f8ad9
⑤ Mock使用
參考下面鏈接
http://qiita.com/jnchito/items/640f17e124ab263a54dd
6、 測試覆蓋率報告
① ?環境搭建
在Gemfile中追加下面的gem。
group :development, :test dogem 'simplecov'
end
然后在cmd工程目錄my-first-api下執行bundle install,安裝追加的gem。
② spec_helper.rb修改
在spec_helper.rb頭部插入下面的語句,覆蓋率測試中要過濾掉spec下的測試代碼。
require 'simplecov'SimpleCov.start do
add_filter "/spec/ "
end
③ 查看報告
在cmd工程目錄my-first-api下,再次執行rspec spec命令,覆蓋率報告就會自動生成再demo/coverage下,用google chrome瀏覽器打開index.html,就可以看到詳細的信息。下面是一個覆蓋率報告的截圖。
7、 結束語
上面我們講述的是Ruby下怎么使用cloud datastore的開發和測試,在Google Cloud Platform上怎么部署產品還有待下一步探索。期間遇到的各種技術問題難題,為了解決這些問題,調查的網站以日語和英語為主,總結的時候也使用了很多日語網站,由于時間有限,沒能一一翻譯過來,給不懂日語的朋友帶來不少困難表示歉意。
附錄1
API工程創建文件list
create
create? README.md
create? Rakefile
create? config.ru
create? .gitignore
create? Gemfile
create? app
create? app/assets/config/manifest.js
create? app/assets/javascripts/application.js
create? app/assets/javascripts/cable.js
create? app/assets/stylesheets/application.css
create? app/channels/application_cable/channel.rb
create? app/channels/application_cable/connection.rb
create? app/controllers/application_controller.rb
create? app/helpers/application_helper.rb
create? app/jobs/application_job.rb
create? app/mailers/application_mailer.rb
create? app/models/application_record.rb
create? app/views/layouts/application.html.erb
create? app/views/layouts/mailer.html.erb
create? app/views/layouts/mailer.text.erb
create? app/assets/images/.keep
create? app/assets/javascripts/channels
create? app/assets/javascripts/channels/.keep
create? app/controllers/concerns/.keep
create? app/models/concerns/.keep
create? bin
create? bin/bundle
create? bin/rails
create? bin/rake
create? bin/setup
create? bin/update
create? config
create? config/routes.rb
create? config/application.rb
create? config/environment.rb
create? config/secrets.yml
create? config/cable.yml
create? config/puma.rb
create? config/environments
create? config/environments/development.rb
create? config/environments/production.rb
create? config/environments/test.rb
create? config/initializers
create? config/initializers/application_controller_renderer.rb
create? config/initializers/assets.rb
create? config/initializers/backtrace_silencers.rb
create? config/initializers/cookies_serializer.rb
create? config/initializers/cors.rb
create? config/initializers/filter_parameter_logging.rb
create? config/initializers/inflections.rb
create? config/initializers/mime_types.rb
create? config/initializers/new_framework_defaults.rb
create? config/initializers/session_store.rb
create? config/initializers/wrap_parameters.rb
create? config/locales
create? config/locales/en.yml
create? config/boot.rb
create? config/database.yml
create? db
create? db/seeds.rb
create? lib
create? lib/tasks
create? lib/tasks/.keep
create? lib/assets
create? lib/assets/.keep
create? log
create? log/.keep
create? public
create? public/404.html
create? public/422.html
create? public/500.html
create? public/apple-touch-icon-precomposed.png
create? public/apple-touch-icon.png
create? public/favicon.ico
create? public/robots.txt
create? test/fixtures
create? test/fixtures/.keep
create? test/fixtures/files
create? test/fixtures/files/.keep
create? test/controllers
create? test/controllers/.keep
create? test/mailers
create? test/mailers/.keep
create? test/models
create? test/models/.keep
create? test/helpers
create? test/helpers/.keep
create? test/integration
create? test/integration/.keep
create? test/test_helper.rb
create? tmp
create? tmp/.keep
create? tmp/cache
create? tmp/cache/assets
create? vendor/assets/stylesheets
create? vendor/assets/stylesheets/.keep
remove? app/assets
remove? lib/assets
remove? tmp/cache/assets
remove? vendor/assets
remove? app/helpers
remove? test/helpers
remove? app/views/layouts/application.html.erb
remove? public/404.html
remove? public/422.html
remove? public/500.html
remove? public/apple-touch-icon-precomposed.png
remove? public/apple-touch-icon.png
remove? public/favicon.ico
remove? app/assets/javascripts
remove? config/initializers/assets.rb
remove? config/initializers/session_store.rb
remove? config/initializers/cookies_serializer.rb
Fetching gem metadata from https://rubygems.org/..........
Fetching version metadata from https://rubygems.org/..
Fetching dependency metadata from https://rubygems.org/.
Resolving dependencies...
Using rake 12.0.0
Using i18n 0.7.0
Using minitest 5.10.1
Using thread_safe 0.3.5
Using builder 3.2.2
Using erubis 2.7.0
Using mini_portile2 2.1.0
Using rack 2.0.1
Using nio4r 1.2.1
Using websocket-extensions 0.1.2
Using mime-types-data 3.2016.0521
Using arel 7.1.4
Using bundler 1.13.6
Using method_source 0.8.2
Using puma 3.6.2
Using thor 0.19.4
Using sqlite3 1.3.12
Gem::RemoteFetcher::FetchError: SSL_connect returned=1 errno=0 state=SSLv3 read
server certificate B: certificate verify failed
(https://rubygems.org/gems/concurrent-ruby-1.0.4.gem)
An error occurred while installing concurrent-ruby (1.0.4), and Bundler cannot
continue.
Make sure that `gem install concurrent-ruby -v '1.0.4'` succeeds before
bundling.
附錄2
active_model_cloud_datastore.rb文件內容:
# frozen_string_literal: truerequire_relative 'cloud_datastore'
require 'active_model'
require 'active_support'
# Integrates ActiveModel with the Google::Cloud::Datastore
module ActiveModelCloudDatastore
extend ActiveSupport::Concern
include ActiveModel::Model
include ActiveModel::Dirty
include ActiveModel::Validations
include ActiveModel::Validations::Callbacks
included do
private_class_method :query_options, :query_sort, :query_property_filter
define_model_callbacks :save, :update, :destroy
attr_accessor :id
end
def attributes
[]
end
# Used by ActiveModel for determining polymorphic routing.
def persisted?
id.present?
end
# Resets the ActiveModel::Dirty tracked changes
def reload!
clear_changes_information
end
# Updates attribute values on the ActiveModel::Model object with the provided params.
# Example, such as submitted form params.
#
# @param [Hash] params
def update_model_attributes(params)
params.each do |name, value|
send "#{name}=", value if respond_to? "#{name}="
end
end
# Builds the Cloud Datastore entity with attributes from the Model object.
#
# @return [Entity] the updated Google::Cloud::Datastore::Entity
def build_entity(parent = nil)
entity = CloudDatastore.dataset.entity(self.class.name, id)
entity.key.parent = parent if parent
attributes.each do |attr|
entity[attr] = instance_variable_get("@#{attr}")
end
entity
end
def save(parent = nil)
run_callbacks :save do
if valid?
entity = build_entity(parent)
success = self.class.retry_on_exception? { CloudDatastore.dataset.save(entity) }
if success
self.id = entity.key.id
return true
end
end
false
end
end
def update(params)
run_callbacks :update do
update_model_attributes(params)
if valid?
entity = build_entity
self.class.retry_on_exception? { CloudDatastore.dataset.save(entity) }
else
false
end
end
end
def destroy
run_callbacks :destroy do
key = CloudDatastore.dataset.key(self.class.name, id)
self.class.retry_on_exception? { CloudDatastore.dataset.delete(key) }
end
end
# Methods defined here will be class methods whenever we 'include DatastoreUtils'.
module ClassMethods
# Queries all objects from Cloud Datastore by named kind and using the provided options.
#
# @param [Hash] options the options to construct the query with.
#
# @option options [Google::Cloud::Datastore::Key] :ancestor filter for inherited results
# @option options [Hash] :where filter, Array in the format [name, operator, value]
#
# @return [Array<Model>] an array of ActiveModel results.
def all(options = {})
query = CloudDatastore.dataset.query(name)
query.ancestor(options[:ancestor]) if options[:ancestor]
query_property_filter(query, options)
entities = retry_on_exception { CloudDatastore.dataset.run(query) }
from_entities(entities.flatten)
end
# Queries objects from Cloud Datastore in batches by named kind and using the provided options.
# When a limit option is provided queries up to the limit and returns results with a cursor.
#
# @param [Hash] options the options to construct the query with. See build_query for options.
#
# @return [Array<Model>, String] an array of ActiveModel results and a cursor that can be used
# to query for additional results.
def find_in_batches(options = {})
next_cursor = nil
query = build_query(options)
if options[:limit]
entities = retry_on_exception { CloudDatastore.dataset.run(query) }
next_cursor = entities.cursor if entities.size == options[:limit]
else
entities = retry_on_exception { CloudDatastore.dataset.run(query) }
end
model_entities = from_entities(entities.flatten)
return model_entities, next_cursor
end
# Retrieves an entity by key and by an optional parent.
#
# @param [Integer or String] id_or_name id or name value of the entity Key.
# @param [Google::Cloud::Datastore::Key] parent the parent Key of the entity.
#
# @return [Entity, nil] a Google::Cloud::Datastore::Entity object or nil.
def find_entity(id_or_name, parent = nil)
key = CloudDatastore.dataset.key(name, id_or_name)
key.parent = parent if parent
retry_on_exception { CloudDatastore.dataset.find(key) }
end
# Find object by ID.
#
# @return [Model, nil] an ActiveModel object or nil.
def find(id)
entity = find_entity(id.to_i)
from_entity(entity)
end
# Find object by parent and ID.
#
# @return [Model, nil] an ActiveModel object or nil.
def find_by_parent(id, parent)
entity = find_entity(id.to_i, parent)
from_entity(entity)
end
def from_entities(entities)
entities.map { |entity| from_entity(entity) }
end
# Translates between Google::Cloud::Datastore::Entity objects and ActiveModel::Model objects.
#
# @param [Entity] entity from Cloud Datastore
# @return [Model] the translated ActiveModel object.
def from_entity(entity)
return if entity.nil?
model_entity = new
model_entity.id = entity.key.id unless entity.key.id.nil?
model_entity.id = entity.key.name unless entity.key.name.nil?
entity.properties.to_hash.each do |name, value|
model_entity.send "#{name}=", value
end
model_entity.reload!
model_entity
end
def exclude_from_index(entity, boolean)
entity.properties.to_h.keys.each do |value|
entity.exclude_from_indexes! value, boolean
end
end
# Constructs a Google::Cloud::Datastore::Query.
#
# @param [Hash] options the options to construct the query with.
#
# @option options [Google::Cloud::Datastore::Key] :ancestor filter for inherited results
# @option options [String] :cursor sets the cursor to start the results at
# @option options [Integer] :limit sets a limit to the number of results to be returned
# @option options [String] :order sort the results by property name
# @option options [String] :desc_order sort the results by descending property name
# @option options [Array] :select retrieve only select properties from the matched entities
# @option options [Hash] :where filter, Array in the format [name, operator, value]
#
# @return [Query] a datastore query.
def build_query(options = {})
query = CloudDatastore.dataset.query(name)
query_options(query, options)
end
def retry_on_exception?
retry_count = 0
sleep_time = 0.5 # 0.5, 1, 2, 4 second between retries
begin
yield
rescue => e
puts "\e[33m[#{e.message.inspect}]\e[0m"
puts 'Rescued exception, retrying...'
sleep sleep_time
sleep_time *= 2
retry_count += 1
return false if retry_count > 3
retry
end
true
end
def retry_on_exception
retry_count = 0
sleep_time = 0.5 # 0.5, 1, 2, 4 second between retries
begin
yield
rescue => e
puts "\e[33m[#{e.message.inspect}]\e[0m"
puts 'Rescued exception, retrying...'
sleep sleep_time
sleep_time *= 2
retry_count += 1
raise e if retry_count > 3
retry
end
end
def log_google_cloud_error
yield
rescue Google::Cloud::Error => e
puts "\e[33m[#{e.message.inspect}]\e[0m"
raise e
end
# private
def query_options(query, options)
query.ancestor(options[:ancestor]) if options[:ancestor]
query.cursor(options[:cursor]) if options[:cursor]
query.limit(options[:limit]) if options[:limit]
query_sort(query, options)
query.select(options[:select]) if options[:select]
query_property_filter(query, options)
end
# Adds sorting to the results by a property name if included in the options.
def query_sort(query, options)
query.order(options[:order]) if options[:order]
query.order(options[:desc_order], :desc) if options[:desc_order]
query
end
# Adds property filters to the query if included in the options.
# Accepts individual or nested Arrays:
# [['superseded', '=', false], ['email', '=', 'something']]
def query_property_filter(query, options)
if options[:where]
opts = options[:where]
if opts[0].is_a?(Array)
opts.each do |opt|
query.where(opt[0], opt[1], opt[2]) unless opt.nil?
end
else
query.where(opts[0], opts[1], opts[2])
end
end
query
end
end
end
?
轉載于:https://www.cnblogs.com/sundongxiang/p/6237805.html
總結
以上是生活随笔為你收集整理的Rails下cloud datastore的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jQuery遍历,数组,集合
- 下一篇: autoitv3点击windows界面