文学フリマでは一箇所で配信,電書フリマでは複数の場所で同じ電書を配信しました。となると次の目標は「異なる場所で,異なる電書を」です。つまり,販売サイトを複数運用して,それぞれ異なる電書を配信します。個々の販売サイトは独立した電書ショップになります。電書の品揃えも売上管理も独立して行う必要があるでしょう。このバージョンは「電書サーバー 秋バージョン」と呼んでいます。
最終回となる今回は,この電書サーバー 秋バージョンと,今後の展望を説明していきます。
マルチアカウント
販売サイトは電書サーバーのアカウントがあれば作成できます。これまでは,アカウントが異なっても同じ電書を配信していました。アカウントごとに異なる電書の品揃えを持つために,秋バージョンではアカウントのモデルを導入して,電書のリストや売上情報などをアカウント毎に持てるようにします。
WardenPlugin
これまではsinatra-authorizationによるBASIC認証を使っていました。idとパスワードをパラメータで受け取って真偽値を返すメソッドがあればいいのでお手軽なのですが,ユーザの新規追加,パスワードのハッシュ化などの機能はありません。BASIC認証なのでログアウトもできません。そこで,もう少し高機能な認証ライブラリとして,sinatra_more付属のWardenPluginを使うことにしました。
WardenPluginを使うと,アカウントの新規作成,パスワードの確認,パスワードをハッシュ化して保存などが簡単にできるようになります。
バグ
ただし,WardenPluginはバグがあります。idからレコードをひくコードでfindメソッドを使っているのです。これはActiveRecordでは問題ありませんが,DataMapperではfindではなくgetを使います。SinatraMore::WardenPlugin::PasswordStrategyの中にあるので,そこは書き換えるなりパッチを当てるなりしてください。
ユーザーモデル
ユーザー用のモデルとしてDenshoUserを新たに作りました。sinatra_moreにはアプリのひながたを生成するsinatra_gemコマンドが付属しています。これを使ってアプリを作るとWardenPluginで利用可能なUserクラス「user.rb」が生成されます。DenshoUserは,それを改造したものです。UserではなくDenshoUserを使うようにするには以下のようにします。
DenshoUserのモデル定義
class DenshoUser
include DataMapper::Resource
has 1, :book_set
has n, :customers
property :id, Serial
property :name, String, :default=>'電書部売店''電書部売店'
property :username, String
property :email, String
property :crypted_password, String, :length=>128128
# これがfalseのうちはログインできない
property :ok, Boolean, :default=>falsefalse
# okをtrueにできる権限
property :admin, Boolean, :default=>falsefalse
# 割引機能を使うか?
property :enable_discount, Boolean, :default=>truetrue
attr_accessor :password, :password_confirmation
before :save, :encrypt_password
validates_presence_of :username, :password, :password_confirmation
validates_confirmation_of :password
validates_uniqueness_of :username
def self.authenticate(username, password)
user = DenshoUser.first(:username => username) username)
user && user.has_password?(password) && user.ok > user : nil
end
def encrypt_password
self.crypted_password = BCrypt::Password.create(password)
end
def has_password?(password)
BCrypt::Password.new(crypted_password) == password
end
end
Userではなく自前のDenshoUserクラスを使うように設定
Dzz::register SinatraMore::WardenPlugin #まずプラグインを登録する
SinatraMore::WardenPlugin::PasswordStrategy.user_class = DenshoUser #DenshoUserクラスを使うように設定
「validates_presence_of :username, :password, :password_confirmation」とバリデーションが指定されているので,passwordとpassword_confirmationの値が一致していないとsaveが失敗します。コンソールからDenshoUserに値をセットするとき,これだと常にpasswordとpassword_confirmationの値が必要なので使いにくいことがあります。たとえばname属性だけを変更したいときなどです。そういう場合にはsaveではなくsave!を使うとバリデーションを回避できます。
ブックセット
アカウント毎に異なる電書を販売するために,ブックセットを導入しました。アカウントで販売する電書のセットがブックセットです。現在の仕様ではひとつのアカウントにつき,ブックセットは一つです。ブックセットには,登録されている電書はどれでも指定できます。
BookSetクラスのモデル定義
class BookSet
include DataMapper::Resource
belongs_to :densho_user
has n, :book_masters, :through => Resource Resource
property :id, Serial
property :name, String, :default=>'販売セット''販売セット'
validates_presence_of :name
end
売り上げ系の画面
売り上げ系の画面(電書別売上,売上詳細,購入者別売上)はアカウント別に表示するようになりました。もちろん売上額もアカウント別に計算しています。
アカウント管理と電書の保護
どの電書を売るかは電書アカウントを持つ人間の判断に任されることになります。まったく知らない人が勝手に電書を売るという事態を避けるために,以下の機能を追加しました。基本的には善意に頼るシステムなので,将来的には変更が必要になるかもしれません。
- DenshoUserは新規登録しただけでは利用できず,admin権限を持った人による承認が必要(具体的にはokフラグをtrueにする)
- 誰でも見ることができる,電書別の売上画面を追加

