File: model.rb

package info (click to toggle)
ruby-diva 2.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 264 kB
  • sloc: ruby: 1,967; sh: 8; makefile: 3
file content (163 lines) | stat: -rw-r--r-- 4,152 bytes parent folder | download
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