1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
# -*- coding: utf-8 -*-
=begin rdoc
いろんなリソースの基底クラス
=end
require 'diva/model_extend'
require 'diva/uri'
require 'diva/spec'
require 'securerandom'
class Diva::Model
include Comparable
extend Diva::ModelExtend
def initialize(args)
@value = args.dup
validate
self.class.store_datum(self)
end
# データをマージする。
# selfにあってotherにもあるカラムはotherの内容で上書きされる。
# 上書き後、データはDataSourceに保存される
def merge(other)
@value.update(other.to_h)
validate
self.class.store_datum(self)
end
# このModelのパーマリンクを返す。
# パーマリンクはWebのURLで、Web上のリソースでない場合はnilを返す。
# ==== Return
# 次のいずれか
# [URI::HTTP|Diva::URI] パーマリンク
# [nil] パーマリンクが存在しない
def perma_link
nil
end
# このModelのURIを返す。
# ==== Return
# [URI::Generic|Diva::URI] パーマリンク
def uri
perma_link || Diva::URI.new("#{self.class.scheme}://#{self.class.host}#{path}")
end
# このModelが、登録されているアカウントのうちいずれかが作成したものであれば true を返す
# ==== Args
# [service] Service | Enumerable 「自分」のService
# ==== Return
# [true] 自分のによって作られたオブジェクトである
# [false] 自分のによって作られたオブジェクトではない
def me?(service=nil)
false
end
def hash
@_hash ||= uri.to_s.hash ^ self.class.hash
end
def <=>(other)
if other.is_a?(Diva::Model)
created - other.created
elsif other.respond_to?(:[]) && other[:created]
created - other[:created]
else
id - other
end
end
def ==(other)
if other.is_a? Diva::Model
self.class == other.class && uri == other.uri
end
end
def eql?(other)
self == other
end
def to_h
self.class.fields.to_h { |f| [f.name, fetch(f.name)] }
end
def to_json(*rest)
self.class.fields.to_h { |f| [f.name, f.dump_for_json(fetch(f.name))] }.to_json(*rest)
end
# カラムの生の内容を返す
def fetch(key)
@value[key.to_sym]
end
alias [] fetch
# カラムに別の値を格納する。
# 格納後、データはDataSourceに保存される
def []=(key, value)
@value[key.to_sym] = value
self.class.store_datum(self)
end
# カラムと型が違うものがある場合、例外を発生させる。
def validate
raise "argument is #{@value}, not Hash" unless @value.is_a?(Hash)
self.class.fields.each do |field|
@value[field.name] = field.type.cast(@value[field.name])
rescue Diva::InvalidTypeError
raise Diva::InvalidTypeError, "#{self.class}.#{field} (#{field.type}): Invalid assign #{@value[field.name].inspect}:#{@value[field.name].class}"
end
end
# キーとして定義されていない値を全て除外した配列を生成して返す。
# また、Modelを子に含んでいる場合、それを外部キーに変換する。
def filtering
datum = to_h
result = {}
self.class.fields.each do |field|
result[field.name] = field.type.cast(datum[field.name])
rescue Diva::InvalidTypeError => exception
raise Diva::InvalidTypeError, "#{exception} in field `#{field}'"
end
result
end
# このインスタンスのタイトル。
def title
fields = self.class.fields.lazy.map(&:name)
if fields.include?(:name)
name.gsub("\n", '')
elsif fields.include?(:description)
description.gsub("\n", '')
else
to_s.gsub("\n", '')
end
end
def dig(key, *args)
return nil unless key.respond_to?(:to_sym)
value = fetch(key)
if value == nil || args.empty?
value
else
value.dig(*args)
end
end
def deconstruct_keys(keys)
if keys
@value.slice(*keys) # 高速化のため、fetchを呼び出していない
else
@value
end
end
private
# URIがデフォルトで使うpath要素
def path
@path ||= "/#{SecureRandom.uuid}"
end
end
|