diva 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/diva.gemspec +4 -0
- data/lib/diva.rb +12 -1
- data/lib/diva/datasource.rb +33 -0
- data/lib/diva/error.rb +19 -0
- data/lib/diva/field.rb +38 -0
- data/lib/diva/field_generator.rb +39 -0
- data/lib/diva/model.rb +147 -0
- data/lib/diva/model_extend.rb +94 -0
- data/lib/diva/spec.rb +9 -0
- data/lib/diva/type.rb +216 -0
- data/lib/diva/uri.rb +131 -0
- data/lib/diva/version.rb +1 -1
- metadata +60 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3d371525ed19621b1a152a2623587783e4802e9
|
4
|
+
data.tar.gz: dfbe31dde289f9399e54ddbb07174652dc41efc8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1b8cded54cb6406710cca50f95947c39f72fa3398ce43a7a0b38e69b7932a9860714a5aa005cf1e561cfdd2bd8cab557b647b036f241898b2f568c8e6f972105
|
7
|
+
data.tar.gz: 81150d4fa04f1b9d4a8c83e754b788362f21ddf441ff5fc0fcc2e84b4bb53f08818fc613c096f874db9412cb1356c504324f26b387c009a228ed114308f14025
|
data/.gitignore
CHANGED
data/diva.gemspec
CHANGED
@@ -21,4 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.12"
|
22
22
|
spec.add_development_dependency "rake", "~> 10.0"
|
23
23
|
spec.add_development_dependency "minitest", "~> 5.0"
|
24
|
+
spec.add_development_dependency "pry"
|
25
|
+
spec.add_development_dependency "simplecov"
|
26
|
+
|
27
|
+
spec.add_dependency "addressable", ">= 2.5", "< 2.6"
|
24
28
|
end
|
data/lib/diva.rb
CHANGED
@@ -1,4 +1,15 @@
|
|
1
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
require 'diva/version'
|
3
|
+
require 'diva/datasource'
|
4
|
+
require 'diva/error'
|
5
|
+
require 'diva/field_generator'
|
6
|
+
require 'diva/field'
|
7
|
+
require 'diva/model'
|
8
|
+
require 'diva/spec'
|
9
|
+
require 'diva/type'
|
10
|
+
require 'diva/uri'
|
11
|
+
require 'diva/version'
|
12
|
+
|
2
13
|
|
3
14
|
module Diva
|
4
15
|
# Your code goes here...
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
=begin rdoc
|
4
|
+
データの保存/復元を実際に担当するデータソース。
|
5
|
+
データソースをモデルにModel::add_data_retrieverにて幾つでも参加させることが出来る。
|
6
|
+
=end
|
7
|
+
module Diva::DataSource
|
8
|
+
USE_ALL = -1 # findbyidの引数。全てのDataSourceから探索する
|
9
|
+
USE_LOCAL_ONLY = -2 # findbyidの引数。ローカルにあるデータのみを使う
|
10
|
+
|
11
|
+
attr_accessor :keys
|
12
|
+
|
13
|
+
# idをもつデータを返す。
|
14
|
+
# もし返せない場合は、nilを返す
|
15
|
+
def findbyid(id, policy)
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
# 取得できたらそのDivaのインスタンスをキーにして実行されるDeferredを返す
|
20
|
+
def idof(id)
|
21
|
+
Thread.new{ findbyid(id) } end
|
22
|
+
alias [] idof
|
23
|
+
|
24
|
+
# データの保存
|
25
|
+
# データ一件保存する。保存に成功したか否かを返す。
|
26
|
+
def store_datum(datum)
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
self.class.to_s
|
32
|
+
end
|
33
|
+
end
|
data/lib/diva/error.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Diva
|
3
|
+
class DivaError < StandardError; end
|
4
|
+
|
5
|
+
class InvalidTypeError < DivaError; end
|
6
|
+
|
7
|
+
class InvalidEntityError < DivaError; end
|
8
|
+
|
9
|
+
# 実装してもしなくてもいいメソッドが実装されておらず、結果を得られない
|
10
|
+
class NotImplementedError < DivaError; end
|
11
|
+
|
12
|
+
# IDやURIなどの一意にリソースを特定する情報を使ってデータソースに問い合わせたが、
|
13
|
+
# 対応する情報が見つからず、Modelを作成できない
|
14
|
+
class ModelNotFoundError < DivaError; end
|
15
|
+
|
16
|
+
# URIとして受け付けられない値を渡された
|
17
|
+
class InvalidURIError < InvalidTypeError; end
|
18
|
+
|
19
|
+
end
|
data/lib/diva/field.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'diva/type'
|
4
|
+
|
5
|
+
=begin rdoc
|
6
|
+
Modelのキーの情報を格納する。
|
7
|
+
キーひとつにつき1つのインスタンスが作られる。
|
8
|
+
=end
|
9
|
+
module Diva
|
10
|
+
class Field
|
11
|
+
attr_reader :name, :type, :required
|
12
|
+
|
13
|
+
# [name] Symbol フィールドの名前
|
14
|
+
# [type] Symbol フィールドのタイプ。:int, :string, :bool, :time のほか、Diva::Modelのサブクラスを指定する
|
15
|
+
# [required] boolean _true_ なら、この項目を必須とする
|
16
|
+
def initialize(name, type, required: false)
|
17
|
+
@name = name.to_sym
|
18
|
+
@type = Diva::Type.optional(Diva::Type(type))
|
19
|
+
@required = !!required
|
20
|
+
end
|
21
|
+
|
22
|
+
def required?
|
23
|
+
required
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_sym
|
27
|
+
name
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
name.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def inspect
|
35
|
+
"#<#{self.class}: #{name}(#{type})#{required ? '*' : ''}>"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class Diva::FieldGenerator
|
4
|
+
def initialize(model_klass)
|
5
|
+
@model_klass = model_klass
|
6
|
+
end
|
7
|
+
|
8
|
+
def int(field_name, required: false)
|
9
|
+
@model_klass.add_field(field_name, type: :int, required: required)
|
10
|
+
end
|
11
|
+
|
12
|
+
def string(field_name, required: false)
|
13
|
+
@model_klass.add_field(field_name, type: :string, required: required)
|
14
|
+
end
|
15
|
+
|
16
|
+
def bool(field_name, required: false)
|
17
|
+
@model_klass.add_field(field_name, type: :bool, required: required)
|
18
|
+
end
|
19
|
+
|
20
|
+
def time(field_name, required: false)
|
21
|
+
@model_klass.add_field(field_name, type: :time, required: required)
|
22
|
+
end
|
23
|
+
|
24
|
+
def uri(field_name, required: false)
|
25
|
+
@model_klass.add_field(field_name, type: :uri, required: required)
|
26
|
+
end
|
27
|
+
|
28
|
+
def has(field_name, type, required: false)
|
29
|
+
@model_klass.add_field(field_name, type: type, required: required)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
|
data/lib/diva/model.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
=begin rdoc
|
3
|
+
いろんなリソースの基底クラス
|
4
|
+
=end
|
5
|
+
|
6
|
+
require 'diva/model_extend'
|
7
|
+
require 'diva/uri'
|
8
|
+
require 'diva/spec'
|
9
|
+
|
10
|
+
require 'securerandom'
|
11
|
+
|
12
|
+
class Diva::Model
|
13
|
+
include Comparable
|
14
|
+
extend Diva::ModelExtend
|
15
|
+
|
16
|
+
def initialize(args)
|
17
|
+
@value = args.dup
|
18
|
+
validate
|
19
|
+
self.class.store_datum(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
# データをマージする。
|
23
|
+
# selfにあってotherにもあるカラムはotherの内容で上書きされる。
|
24
|
+
# 上書き後、データはDataSourceに保存される
|
25
|
+
def merge(other)
|
26
|
+
@value.update(other.to_hash)
|
27
|
+
validate
|
28
|
+
self.class.store_datum(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
# このModelのパーマリンクを返す。
|
32
|
+
# パーマリンクはWebのURLで、Web上のリソースでない場合はnilを返す。
|
33
|
+
# ==== Return
|
34
|
+
# 次のいずれか
|
35
|
+
# [URI::HTTP|Diva::URI] パーマリンク
|
36
|
+
# [nil] パーマリンクが存在しない
|
37
|
+
def perma_link
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# このModelのURIを返す。
|
42
|
+
# ==== Return
|
43
|
+
# [URI::Generic|Diva::URI] パーマリンク
|
44
|
+
def uri
|
45
|
+
perma_link || Diva::URI.new("#{self.class.scheme}://#{self.class.host}#{path}")
|
46
|
+
end
|
47
|
+
|
48
|
+
# このModelが、登録されているアカウントのうちいずれかが作成したものであれば true を返す
|
49
|
+
# ==== Args
|
50
|
+
# [service] Service | Enumerable 「自分」のService
|
51
|
+
# ==== Return
|
52
|
+
# [true] 自分のによって作られたオブジェクトである
|
53
|
+
# [false] 自分のによって作られたオブジェクトではない
|
54
|
+
def me?(service=nil)
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
def hash
|
59
|
+
@_hash ||= self.uri.to_s.hash ^ self.class.hash
|
60
|
+
end
|
61
|
+
|
62
|
+
def <=>(other)
|
63
|
+
if other.is_a?(Diva::Model)
|
64
|
+
created - other.created
|
65
|
+
elsif other.respond_to?(:[]) and other[:created]
|
66
|
+
created - other[:created]
|
67
|
+
else
|
68
|
+
id - other
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def ==(other)
|
73
|
+
if other.is_a? Diva::Model
|
74
|
+
self.class == other.class && uri == other.uri
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def eql?(other)
|
79
|
+
self == other
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_hash
|
83
|
+
Hash[self.class.fields.map{|f| [f.name, fetch(f.name)] }]
|
84
|
+
end
|
85
|
+
|
86
|
+
# カラムの生の内容を返す
|
87
|
+
def fetch(key)
|
88
|
+
@value[key.to_sym]
|
89
|
+
end
|
90
|
+
alias [] fetch
|
91
|
+
|
92
|
+
# カラムに別の値を格納する。
|
93
|
+
# 格納後、データはDataSourceに保存される
|
94
|
+
def []=(key, value)
|
95
|
+
@value[key.to_sym] = value
|
96
|
+
self.class.store_datum(self)
|
97
|
+
value
|
98
|
+
end
|
99
|
+
|
100
|
+
# カラムと型が違うものがある場合、例外を発生させる。
|
101
|
+
def validate
|
102
|
+
raise RuntimeError, "argument is #{@value}, not Hash" if not @value.is_a?(Hash)
|
103
|
+
self.class.fields.each do |field|
|
104
|
+
begin
|
105
|
+
@value[field.name] = field.type.cast(@value[field.name])
|
106
|
+
rescue Diva::InvalidTypeError => err
|
107
|
+
raise Diva::InvalidTypeError, "#{err} in field `#{field}'"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# キーとして定義されていない値を全て除外した配列を生成して返す。
|
113
|
+
# また、Modelを子に含んでいる場合、それを外部キーに変換する。
|
114
|
+
def filtering
|
115
|
+
datum = self.to_hash
|
116
|
+
result = Hash.new
|
117
|
+
self.class.fields.each do |field|
|
118
|
+
begin
|
119
|
+
result[field.name] = field.type.cast(datum[field.name])
|
120
|
+
rescue Diva::InvalidTypeError => err
|
121
|
+
raise Diva::InvalidTypeError, "#{err} in field `#{field}'"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
result
|
125
|
+
end
|
126
|
+
|
127
|
+
# このインスタンスのタイトル。
|
128
|
+
def title
|
129
|
+
fields = self.class.fields.lazy.map(&:name)
|
130
|
+
case
|
131
|
+
when fields.include?(:name)
|
132
|
+
name.gsub("\n", '')
|
133
|
+
when fields.include?(:description)
|
134
|
+
description.gsub("\n", '')
|
135
|
+
else
|
136
|
+
to_s.gsub("\n", '')
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
# URIがデフォルトで使うpath要素
|
142
|
+
def path
|
143
|
+
@path ||= "/#{SecureRandom.uuid}"
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'diva/field'
|
4
|
+
require 'diva/type'
|
5
|
+
|
6
|
+
=begin rdoc
|
7
|
+
Diva::Model のクラスメソッド
|
8
|
+
=end
|
9
|
+
module Diva::ModelExtend
|
10
|
+
extend Gem::Deprecate
|
11
|
+
|
12
|
+
attr_reader :slug, :spec
|
13
|
+
|
14
|
+
# Modelのインスタンスのuriスキーム。オーバライドして適切な値にする
|
15
|
+
# ==== Return
|
16
|
+
# [String] URIスキーム
|
17
|
+
def scheme
|
18
|
+
@_scheme ||= self.to_s.split('::',2).first.gsub(/\W/,'').downcase.freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
# Modelのインスタンスのホスト名。オーバライドして適切な値にする
|
22
|
+
# ==== Return
|
23
|
+
# [String] ホスト名
|
24
|
+
def host
|
25
|
+
@_host ||= self.to_s.split('::',2).last.split('::').reverse.join('.').gsub(/[^\w\.]/,'').downcase.freeze
|
26
|
+
end
|
27
|
+
|
28
|
+
# Modelにフィールドを追加する。
|
29
|
+
# ==== Args
|
30
|
+
# [field_name] Symbol フィールドの名前
|
31
|
+
# [type] Symbol フィールドのタイプ。:int, :string, :bool, :time のほか、Diva::Modelのサブクラスを指定する
|
32
|
+
# [required] boolean _true_ なら、この項目を必須とする
|
33
|
+
def add_field(field, type: nil, required: false)
|
34
|
+
if field.is_a?(Symbol)
|
35
|
+
field = Diva::Field.new(field, type, required: required)
|
36
|
+
end
|
37
|
+
(@fields ||= []) << field
|
38
|
+
define_method(field.name) do
|
39
|
+
@value[field.name]
|
40
|
+
end
|
41
|
+
|
42
|
+
define_method("#{field.name}?") do
|
43
|
+
!!@value[field.name]
|
44
|
+
end
|
45
|
+
|
46
|
+
define_method("#{field.name}=") do |value|
|
47
|
+
@value[field.name] = field.type.cast(value)
|
48
|
+
self.class.store_datum(self)
|
49
|
+
value
|
50
|
+
end
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def fields
|
55
|
+
@fields ||= []
|
56
|
+
end
|
57
|
+
alias :keys :fields
|
58
|
+
deprecate :keys, "fields", 2018, 02
|
59
|
+
|
60
|
+
#
|
61
|
+
# プライベートクラスメソッド
|
62
|
+
#
|
63
|
+
|
64
|
+
def field
|
65
|
+
Diva::FieldGenerator.new(self)
|
66
|
+
end
|
67
|
+
|
68
|
+
# URIに対応するリソースの内容を持ったModelを作成する。
|
69
|
+
# URIに対応する情報はネットワーク上などから取得される場合もある。そういった場合はこのメソッドは
|
70
|
+
# Delayer::Deferred::Deferredable を返す可能性がある。
|
71
|
+
# とくにオーバライドしない場合、このメソッドは常に例外 Diva::NotImplementedError を投げる。
|
72
|
+
# ==== Args
|
73
|
+
# [uri] _handle_ メソッドで指定したいずれかの条件に一致するURI
|
74
|
+
# ==== Return
|
75
|
+
# [Delayer::Deferred::Deferredable]
|
76
|
+
# ネットワークアクセスを行って取得するなど取得に時間がかかる場合
|
77
|
+
# [self]
|
78
|
+
# すぐにModelを生成できる場合、そのModel
|
79
|
+
# ==== Raise
|
80
|
+
# [Diva::NotImplementedError]
|
81
|
+
# このModelでは、find_by_uriが実装されていない
|
82
|
+
# [Diva::ModelNotFoundError]
|
83
|
+
# _uri_ に対応するリソースが見つからなかった
|
84
|
+
def find_by_uri(uri)
|
85
|
+
raise Diva::NotImplementedError, "#{self}.find_by_uri does not implement."
|
86
|
+
end
|
87
|
+
|
88
|
+
# Modelが生成・更新された時に呼ばれるコールバックメソッドです
|
89
|
+
def store_datum(retriever); end
|
90
|
+
|
91
|
+
def container_class
|
92
|
+
Array
|
93
|
+
end
|
94
|
+
end
|
data/lib/diva/spec.rb
ADDED
data/lib/diva/type.rb
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
=begin rdoc
|
4
|
+
Modelの各キーに格納できる値の制約。
|
5
|
+
この制約に満たない場合は、アトミックな制約であれば値の変換が行われ、そうでない場合は
|
6
|
+
Diva::InvalidTypeError 例外を投げる。
|
7
|
+
|
8
|
+
これは新しくインスタンスなどを作らず、INT、FLOATなどのプリセットを利用する。
|
9
|
+
|
10
|
+
== 設定できる制約
|
11
|
+
Modelフィールドの制約には以下のようなものがある。
|
12
|
+
|
13
|
+
=== アトミックな制約
|
14
|
+
以下のような値は、DivaのModelフィールドにおいてはアトミックな制約と呼び、そのまま格納できる。
|
15
|
+
[INT] 数値(Integer)
|
16
|
+
[FLOAT] 少数(Float)
|
17
|
+
[BOOL] 真理値(true|false)
|
18
|
+
[STRING] 文字列(String)
|
19
|
+
[TIME] 時刻(Time)
|
20
|
+
[URI] URI(Diva::URI|URI::Generic|Addressable::URI)
|
21
|
+
|
22
|
+
=== Model
|
23
|
+
Diva::Modelのサブクラスであれば、それを制約とすることができる。
|
24
|
+
|
25
|
+
=== 配列
|
26
|
+
アトミックな制約またはModel制約を満たした値の配列を格納することができる。
|
27
|
+
配列の全ての要素が設定された制約を満たしていれば、配列制約が満たされたことになる。
|
28
|
+
|
29
|
+
=end
|
30
|
+
module Diva::Type
|
31
|
+
extend self
|
32
|
+
|
33
|
+
def model_of(model)
|
34
|
+
ModelType.new(model)
|
35
|
+
end
|
36
|
+
|
37
|
+
def array_of(type)
|
38
|
+
ArrayType.new(type)
|
39
|
+
end
|
40
|
+
|
41
|
+
def optional(type)
|
42
|
+
OptionalType.new(type)
|
43
|
+
end
|
44
|
+
|
45
|
+
# 全てのType共通のスーパークラス
|
46
|
+
class MetaType
|
47
|
+
attr_reader :name
|
48
|
+
|
49
|
+
def initialize(name, *rest, &cast)
|
50
|
+
@name = name.to_sym
|
51
|
+
if cast
|
52
|
+
define_singleton_method :cast, &cast
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def cast(value)
|
57
|
+
value
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
name.to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
def inspect
|
65
|
+
"Diva::Type(#{to_s})"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class AtomicType < MetaType
|
70
|
+
end
|
71
|
+
|
72
|
+
INT = AtomicType.new(:int) do |v|
|
73
|
+
case v
|
74
|
+
when Integer
|
75
|
+
v
|
76
|
+
when Numeric, String, Time
|
77
|
+
v.to_i
|
78
|
+
when TrueClass
|
79
|
+
1
|
80
|
+
when FalseClass
|
81
|
+
0
|
82
|
+
else
|
83
|
+
raise Diva::InvalidTypeError, "The value is not a `#{name}'."
|
84
|
+
end
|
85
|
+
end
|
86
|
+
FLOAT = AtomicType.new(:float) do |v|
|
87
|
+
case v
|
88
|
+
when Float
|
89
|
+
v
|
90
|
+
when Numeric, String, Time
|
91
|
+
v.to_f
|
92
|
+
else
|
93
|
+
raise Diva::InvalidTypeError, "The value is not a `#{name}'."
|
94
|
+
end
|
95
|
+
end
|
96
|
+
BOOL = AtomicType.new(:bool) do |v|
|
97
|
+
case v
|
98
|
+
when TrueClass, FalseClass
|
99
|
+
v
|
100
|
+
when String
|
101
|
+
!v.empty?
|
102
|
+
when Integer
|
103
|
+
v != 0
|
104
|
+
else
|
105
|
+
raise Diva::InvalidTypeError, "The value is not a `#{name}'."
|
106
|
+
end
|
107
|
+
end
|
108
|
+
STRING = AtomicType.new(:string) do |v|
|
109
|
+
case v
|
110
|
+
when Diva::Model, Enumerable
|
111
|
+
raise Diva::InvalidTypeError, "The value is not a `#{name}'."
|
112
|
+
else
|
113
|
+
v.to_s
|
114
|
+
end
|
115
|
+
end
|
116
|
+
TIME = AtomicType.new(:time) do |v|
|
117
|
+
case v
|
118
|
+
when Time
|
119
|
+
v
|
120
|
+
when Integer, Float
|
121
|
+
Time.at(v)
|
122
|
+
when String
|
123
|
+
Time.new(v)
|
124
|
+
else
|
125
|
+
raise Diva::InvalidTypeError, "The value is not a `#{name}'."
|
126
|
+
end
|
127
|
+
end
|
128
|
+
URI = AtomicType.new(:uri) do |v|
|
129
|
+
case v
|
130
|
+
when Diva::URI, Addressable::URI, ::URI::Generic
|
131
|
+
v
|
132
|
+
when String
|
133
|
+
Diva::URI.new(v)
|
134
|
+
else
|
135
|
+
raise Diva::InvalidTypeError, "The value is not a `#{name}'."
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class ModelType < MetaType
|
140
|
+
attr_reader :model
|
141
|
+
def initialize(model, *rest, &cast)
|
142
|
+
super(:model, *rest)
|
143
|
+
@model = model
|
144
|
+
end
|
145
|
+
|
146
|
+
def cast(value)
|
147
|
+
raise Diva::InvalidTypeError, "The value is not a `#{model}'." unless value.is_a?(model)
|
148
|
+
value
|
149
|
+
end
|
150
|
+
|
151
|
+
def to_s
|
152
|
+
"#{model} #{name}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class ArrayType < MetaType
|
157
|
+
def initialize(type)
|
158
|
+
super("#{type.name}_array")
|
159
|
+
@type = type
|
160
|
+
end
|
161
|
+
|
162
|
+
def cast(value)
|
163
|
+
raise Diva::InvalidTypeError, "The value is not a `#{name}'." unless value.is_a?(Enumerable)
|
164
|
+
value.to_a.map(&@type.method(:cast))
|
165
|
+
end
|
166
|
+
|
167
|
+
def to_s
|
168
|
+
"Array of #{type.to_s}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class OptionalType < MetaType
|
173
|
+
def initialize(type)
|
174
|
+
super("optional_#{type.name}")
|
175
|
+
@type = type
|
176
|
+
end
|
177
|
+
|
178
|
+
def cast(value)
|
179
|
+
if value.nil?
|
180
|
+
value
|
181
|
+
else
|
182
|
+
@type.cast(value)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def to_s
|
187
|
+
"#{type.to_s}|nil"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
module Diva
|
194
|
+
def self.Type(type)
|
195
|
+
case type
|
196
|
+
when Diva::Type::MetaType
|
197
|
+
type
|
198
|
+
when :int
|
199
|
+
Diva::Type::INT
|
200
|
+
when :float
|
201
|
+
Diva::Type::FLOAT
|
202
|
+
when :bool
|
203
|
+
Diva::Type::BOOL
|
204
|
+
when :string
|
205
|
+
Diva::Type::STRING
|
206
|
+
when :time
|
207
|
+
Diva::Type::TIME
|
208
|
+
when :uri
|
209
|
+
Diva::Type::URI
|
210
|
+
when ->x{x.superclass == Diva::Model}
|
211
|
+
Diva::Type.model_of(type)
|
212
|
+
when Array
|
213
|
+
Diva::Type.array_of(type.first)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
data/lib/diva/uri.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
=begin rdoc
|
4
|
+
=Model用のURIクラス
|
5
|
+
|
6
|
+
mikutterでは、 URI や Addressable::URI の代わりに、このクラスを使用します。
|
7
|
+
URI や Addressable::URI に比べて、次のような特徴があります。
|
8
|
+
|
9
|
+
* コンストラクタに文字列を渡している場合、 _to_s_ がその文字列を返す。
|
10
|
+
* 正規化しないかわりに高速に動作します。
|
11
|
+
* Diva::URI のインスタンスは URI と同じように使える
|
12
|
+
* unicode文字などが入っていて URI では表現できない場合、 Addressable::URI を使う
|
13
|
+
* Addressable::URIでないと表現できないURIであればそちらを使うという判断を自動で行う
|
14
|
+
|
15
|
+
== 使い方
|
16
|
+
Diva::URI() メソッドの引数にString, URI, Addressable::URI, Hash, Diva::URIのいずれかを与えます。
|
17
|
+
|
18
|
+
[String] uriの文字列(ex: "http://mikutter.hachune.net/")
|
19
|
+
[URI] URI のインスタンス
|
20
|
+
[Addressable::URI] Addressable::URI のインスタンス
|
21
|
+
[Hash] これを引数にURI::Generic.build に渡すのと同じ形式の Hash
|
22
|
+
[Diva::URI] 即座にこれ自身を返す
|
23
|
+
|
24
|
+
== 例
|
25
|
+
|
26
|
+
Diva::URI("http://mikutter.hachune.net/")
|
27
|
+
Diva::URI(URI::Generic.build(scheme: 'http', host: 'mikutter.hachune.net'))
|
28
|
+
=end
|
29
|
+
|
30
|
+
require 'uri'
|
31
|
+
require 'addressable/uri'
|
32
|
+
|
33
|
+
class Diva::URI
|
34
|
+
def initialize(uri)
|
35
|
+
case uri.freeze
|
36
|
+
when URI, Addressable::URI
|
37
|
+
@uri = uri
|
38
|
+
when String
|
39
|
+
@uri_string = uri
|
40
|
+
when Hash
|
41
|
+
@uri_hash = uri
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def ==(other)
|
46
|
+
case other
|
47
|
+
when URI, Addressable::URI
|
48
|
+
other == to_uri
|
49
|
+
when Diva::URI
|
50
|
+
if has_string? or other.has_string?
|
51
|
+
to_s == other.to_s
|
52
|
+
else
|
53
|
+
other.to_uri == to_uri
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def hash
|
59
|
+
to_s.hash ^ self.class.hash
|
60
|
+
end
|
61
|
+
|
62
|
+
def has_string?
|
63
|
+
!!@uri_string
|
64
|
+
end
|
65
|
+
|
66
|
+
def has_uri?
|
67
|
+
!!@uri
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
@uri_string ||= to_uri.to_s.freeze
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_uri
|
75
|
+
@uri ||= generate_uri.freeze
|
76
|
+
end
|
77
|
+
|
78
|
+
def scheme
|
79
|
+
if has_string? and !has_uri?
|
80
|
+
match = @uri_string.match(%r<\A(\w+):>)
|
81
|
+
if match
|
82
|
+
match[1]
|
83
|
+
else
|
84
|
+
to_uri.scheme
|
85
|
+
end
|
86
|
+
else
|
87
|
+
to_uri.scheme
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def freeze
|
92
|
+
unless frozen?
|
93
|
+
to_uri
|
94
|
+
to_s
|
95
|
+
end
|
96
|
+
super
|
97
|
+
end
|
98
|
+
|
99
|
+
def respond_to?(method)
|
100
|
+
super or to_uri.respond_to?(method)
|
101
|
+
end
|
102
|
+
|
103
|
+
def method_missing(method, *rest, &block)
|
104
|
+
to_uri.__send__(method, *rest, &block)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def generate_uri
|
110
|
+
if @uri
|
111
|
+
@uri
|
112
|
+
elsif @uri_string
|
113
|
+
@uri = generate_uri_by_string
|
114
|
+
elsif @uri_hash
|
115
|
+
@uri = generate_uri_by_hash
|
116
|
+
end
|
117
|
+
@uri
|
118
|
+
end
|
119
|
+
|
120
|
+
def generate_uri_by_string
|
121
|
+
URI.parse(@uri_string)
|
122
|
+
rescue URI::InvalidComponentError
|
123
|
+
Addressable::URI.parse(@uri_string)
|
124
|
+
end
|
125
|
+
|
126
|
+
def generate_uri_by_hash
|
127
|
+
URI::Generic.build(@uri_hash)
|
128
|
+
rescue URI::InvalidComponentError
|
129
|
+
Addressable::URI.new(@uri_hash)
|
130
|
+
end
|
131
|
+
end
|
data/lib/diva/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: diva
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Toshiaki Asai
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-02-
|
11
|
+
date: 2017-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,54 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: addressable
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.5'
|
90
|
+
- - "<"
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '2.6'
|
93
|
+
type: :runtime
|
94
|
+
prerelease: false
|
95
|
+
version_requirements: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '2.5'
|
100
|
+
- - "<"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '2.6'
|
55
103
|
description:
|
56
104
|
email:
|
57
105
|
- toshi.alternative@gmail.com
|
@@ -69,6 +117,15 @@ files:
|
|
69
117
|
- bin/setup
|
70
118
|
- diva.gemspec
|
71
119
|
- lib/diva.rb
|
120
|
+
- lib/diva/datasource.rb
|
121
|
+
- lib/diva/error.rb
|
122
|
+
- lib/diva/field.rb
|
123
|
+
- lib/diva/field_generator.rb
|
124
|
+
- lib/diva/model.rb
|
125
|
+
- lib/diva/model_extend.rb
|
126
|
+
- lib/diva/spec.rb
|
127
|
+
- lib/diva/type.rb
|
128
|
+
- lib/diva/uri.rb
|
72
129
|
- lib/diva/version.rb
|
73
130
|
homepage: https://github.com/toshia/diva
|
74
131
|
licenses:
|
@@ -90,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
147
|
version: '0'
|
91
148
|
requirements: []
|
92
149
|
rubyforge_project:
|
93
|
-
rubygems_version: 2.
|
150
|
+
rubygems_version: 2.6.8
|
94
151
|
signing_key:
|
95
152
|
specification_version: 4
|
96
153
|
summary: Implementation of expression for handling things.
|