typed 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/README.rdoc +20 -0
- data/lib/typed.rb +1 -0
- data/lib/typed/scala.rb +90 -27
- data/lib/typed/schema.rb +4 -0
- data/lib/typed/version.rb +1 -1
- data/spec/scala_spec.rb +219 -32
- data/spec/schema_spec.rb +18 -0
- data/spec/spec_helper.rb +16 -0
- metadata +4 -4
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -82,6 +82,26 @@ We need some typed variables to avoid silly and stealth mistakes.
|
|
82
82
|
=> 1
|
83
83
|
|
84
84
|
|
85
|
+
== Experimental : Typed::Scala
|
86
|
+
|
87
|
+
Love 'val', 'var' of Scala!!
|
88
|
+
|
89
|
+
# [NOTE] val and var must be written in source files.
|
90
|
+
% cat user.rb
|
91
|
+
class User
|
92
|
+
include Typed::Scala
|
93
|
+
val nick = String
|
94
|
+
var pass = String
|
95
|
+
end
|
96
|
+
% bundle exec irb -r typed -r user
|
97
|
+
>> u = User.new
|
98
|
+
>> u.nick
|
99
|
+
Typed::NotDefined: 'nick' is not initialized
|
100
|
+
>> u.nick = "maiha"
|
101
|
+
>> u.nick = "no name"
|
102
|
+
=> Typed::FixedValue: reassignment to nick
|
103
|
+
|
104
|
+
|
85
105
|
== REQUIREMENTS:
|
86
106
|
|
87
107
|
* activesupport gem
|
data/lib/typed.rb
CHANGED
data/lib/typed/scala.rb
CHANGED
@@ -1,59 +1,122 @@
|
|
1
1
|
module Typed
|
2
2
|
module Scala
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
|
4
|
+
######################################################################
|
5
|
+
### instance methods
|
6
|
+
|
7
|
+
def attrs
|
8
|
+
@attrs ||= Typed::Scala::Variables.build_attrs(self.class)
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
if attrs.schema.exist?(key)
|
13
|
+
attrs[key.to_s]
|
14
|
+
else
|
15
|
+
raise Typed::NotDefined, "#{key} is not a member of #{self.class}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(key, val)
|
20
|
+
if attrs.schema.exist?(key)
|
21
|
+
if self.class.vals[key.to_s] and attrs.exist?(key)
|
22
|
+
raise Typed::FixedValue, "reassignment to #{key}"
|
9
23
|
end
|
10
|
-
|
24
|
+
attrs[key.to_s] = val
|
25
|
+
else
|
26
|
+
raise Typed::NotDefined, "#{key} is not a member of #{self.class}"
|
11
27
|
end
|
12
|
-
return @vars
|
13
28
|
end
|
14
29
|
|
15
|
-
|
16
|
-
|
30
|
+
######################################################################
|
31
|
+
### provided api
|
32
|
+
|
33
|
+
module Val
|
34
|
+
def vals
|
35
|
+
@typed_scala_vals ||= Typed::Scala::Variables.build_variables(self, :val)
|
36
|
+
end
|
37
|
+
|
38
|
+
def val(obj)
|
39
|
+
Typed::Scala::Variables.apply(self, :val, caller[0], obj)
|
40
|
+
end
|
17
41
|
end
|
18
42
|
|
19
43
|
module Var
|
44
|
+
def vars
|
45
|
+
@typed_scala_vars ||= Typed::Scala::Variables.build_variables(self, :var)
|
46
|
+
end
|
47
|
+
|
48
|
+
def var(obj)
|
49
|
+
Typed::Scala::Variables.apply(self, :var, caller[0], obj)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
######################################################################
|
54
|
+
### class schema
|
55
|
+
|
56
|
+
module Variables
|
20
57
|
ParseError = Class.new(SyntaxError)
|
21
|
-
|
22
|
-
|
58
|
+
|
59
|
+
def self.apply(klass, type, caller, obj)
|
60
|
+
name = parse(klass, type, caller)
|
61
|
+
define(klass, type, name, obj)
|
23
62
|
end
|
24
63
|
|
25
|
-
def
|
26
|
-
|
64
|
+
def self.define(klass, type, name, obj)
|
65
|
+
vars = klass.__send__("#{type}s")
|
66
|
+
vars[name] = obj
|
27
67
|
|
28
|
-
|
29
|
-
|
68
|
+
klass.class_eval do
|
69
|
+
define_method(name) { self[name.to_s] }
|
70
|
+
define_method("#{name}=") {|v| self[name.to_s] = v }
|
71
|
+
end
|
30
72
|
end
|
31
73
|
|
32
|
-
def
|
74
|
+
def self.parse(klass, type, caller)
|
33
75
|
# "/tmp/adsvr/dsl.rb:23"
|
34
76
|
case caller
|
35
77
|
when %r{^(.*?):(\d+)}o
|
36
|
-
file
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
78
|
+
file = $1
|
79
|
+
lineno = $2.to_i
|
80
|
+
|
81
|
+
lines = (@lines ||= {})[klass] ||= File.readlines(file)
|
82
|
+
case lines[lineno-1].to_s
|
83
|
+
when /^\s*#{type}\s+(\S+)\s+=/
|
41
84
|
return $1
|
42
85
|
else
|
43
|
-
raise ParseError, "#{self} from
|
86
|
+
raise ParseError, "#{self} from #{caller}"
|
44
87
|
end
|
45
88
|
else
|
46
|
-
raise ParseError, "#{self} from caller:#{caller
|
89
|
+
raise ParseError, "#{self} from caller:#{caller}"
|
47
90
|
end
|
48
91
|
end
|
49
92
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
93
|
+
def self.build_attrs(klass)
|
94
|
+
attrs = Typed::Hash.new
|
95
|
+
klass.vals.each_pair{|name, obj| attrs[name] = obj}
|
96
|
+
klass.vars.each_pair{|name, obj| attrs[name] = obj}
|
97
|
+
return attrs
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.build_variables(klass, type)
|
101
|
+
variables = ActiveSupport::OrderedHash.new
|
102
|
+
|
103
|
+
klass.ancestors[1 .. -1].select{|k| k < Typed::Scala}.reverse.each do |k|
|
104
|
+
k.instance_eval("[@typed_scala_#{type}s].compact").each do |hash|
|
105
|
+
variables.merge!(hash)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
return variables
|
53
110
|
end
|
54
111
|
end
|
55
112
|
|
113
|
+
######################################################################
|
114
|
+
### module
|
115
|
+
|
56
116
|
def self.included(klass)
|
117
|
+
super
|
118
|
+
|
119
|
+
klass.extend Scala::Val
|
57
120
|
klass.extend Scala::Var
|
58
121
|
end
|
59
122
|
end
|
data/lib/typed/schema.rb
CHANGED
data/lib/typed/version.rb
CHANGED
data/spec/scala_spec.rb
CHANGED
@@ -1,56 +1,243 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Typed::Scala do
|
4
|
-
before {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
subject { user}
|
9
|
-
|
10
|
-
def write_and_load(code)
|
4
|
+
before { @loaded = [] }
|
5
|
+
after { @loaded.each{|klass| Object.__send__(:remove_const, klass) if Object.const_defined?(klass) } }
|
6
|
+
|
7
|
+
def source(klass, code)
|
11
8
|
path = tmp_path("scala/inline.rb")
|
12
9
|
path.parent.mkpath
|
13
10
|
path.open("w+"){|f| f.puts(code) }
|
14
11
|
load(path.to_s)
|
12
|
+
@loaded << klass
|
15
13
|
end
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
15
|
+
######################################################################
|
16
|
+
### Basic usage
|
17
|
+
|
18
|
+
describe "User" do
|
19
|
+
before { source("User", <<-EOF)
|
20
|
+
class User
|
21
|
+
include Typed::Scala
|
22
|
+
|
23
|
+
val key = String
|
24
|
+
var name = String
|
25
|
+
var age = Fixnum
|
26
|
+
end
|
26
27
|
EOF
|
27
28
|
}
|
29
|
+
|
30
|
+
context "(class)" do
|
31
|
+
subject { User }
|
32
|
+
its(:vals) { should be_kind_of ActiveSupport::OrderedHash }
|
33
|
+
its(:vals) { should == { "key" => String } }
|
34
|
+
its(:vars) { should be_kind_of ActiveSupport::OrderedHash }
|
35
|
+
its(:vars) { should == { "name" => String, "age" => Fixnum } }
|
36
|
+
end
|
37
|
+
|
38
|
+
context "(instance)" do
|
39
|
+
let(:user) { User.new }
|
40
|
+
subject { user}
|
41
|
+
|
42
|
+
it { should respond_to(:key) }
|
43
|
+
it { should respond_to(:name) }
|
44
|
+
it { should respond_to(:age) }
|
45
|
+
it { should_not respond_to(:xxx) }
|
46
|
+
it { should respond_to(:attrs) }
|
47
|
+
it { should respond_to(:[]) }
|
48
|
+
it { should respond_to(:[]=) }
|
28
49
|
|
29
|
-
|
30
|
-
|
31
|
-
|
50
|
+
describe "#name=" do
|
51
|
+
specify "accept 'maiha'" do
|
52
|
+
(user.name = 'maiha').should == 'maiha'
|
53
|
+
end
|
32
54
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
} }
|
55
|
+
specify "reject 100" do
|
56
|
+
lambda { user.name = 100 }.should raise_error(TypeError)
|
57
|
+
end
|
58
|
+
end
|
38
59
|
end
|
60
|
+
end
|
39
61
|
|
40
|
-
|
41
|
-
|
42
|
-
|
62
|
+
######################################################################
|
63
|
+
### Two files at same time
|
64
|
+
|
65
|
+
context "(two files)" do
|
66
|
+
before { source("A", <<-EOF)
|
67
|
+
class A
|
68
|
+
include Typed::Scala
|
69
|
+
val key = String
|
70
|
+
var val = String
|
71
|
+
end
|
72
|
+
EOF
|
73
|
+
}
|
74
|
+
|
75
|
+
before { source("B", <<-EOF)
|
76
|
+
class B
|
77
|
+
include Typed::Scala
|
78
|
+
val key = Fixnum
|
79
|
+
var url = String
|
43
80
|
end
|
81
|
+
EOF
|
82
|
+
}
|
83
|
+
|
84
|
+
specify "should be independent" do
|
85
|
+
A.vals.should == { "key" => String }
|
86
|
+
A.vars.should == { "val" => String }
|
87
|
+
B.vals.should == { "key" => Fixnum }
|
88
|
+
B.vars.should == { "url" => String }
|
89
|
+
end
|
90
|
+
|
91
|
+
specify "accessors" do
|
92
|
+
a = A.new
|
93
|
+
b = B.new
|
94
|
+
|
95
|
+
a.key = "foo"
|
96
|
+
a.val = "xyz"
|
97
|
+
b.key = 10000
|
98
|
+
b.url = "http"
|
99
|
+
|
100
|
+
a.key.should == "foo"
|
101
|
+
a.val.should == "xyz"
|
102
|
+
a["key"].should == "foo"
|
103
|
+
a["val"].should == "xyz"
|
104
|
+
|
105
|
+
b.key.should == 10000
|
106
|
+
b.url.should == "http"
|
107
|
+
b["key"].should == 10000
|
108
|
+
b["url"].should == "http"
|
109
|
+
end
|
110
|
+
|
111
|
+
context "read non defined field" do
|
112
|
+
subject { lambda { A.new["xxx"] } }
|
113
|
+
it { should raise_error(Typed::NotDefined) }
|
114
|
+
it { should raise_error(/xxx is not a member of A/) }
|
44
115
|
end
|
45
116
|
|
46
|
-
|
47
|
-
|
48
|
-
|
117
|
+
context "read defined but not initiaized field" do
|
118
|
+
context "[key]" do
|
119
|
+
subject { lambda { A.new["key"] } }
|
120
|
+
it { should raise_error(Typed::NotDefined) }
|
121
|
+
it { should raise_error(/'key' is not initialized/) }
|
49
122
|
end
|
50
123
|
|
51
|
-
|
52
|
-
lambda {
|
124
|
+
context "#key" do
|
125
|
+
subject { lambda { A.new.key } }
|
126
|
+
it { should raise_error(Typed::NotDefined) }
|
127
|
+
it { should raise_error(/'key' is not initialized/) }
|
53
128
|
end
|
54
129
|
end
|
130
|
+
|
131
|
+
context "write non defined field" do
|
132
|
+
subject { lambda { A.new["xxx"] = 1 } }
|
133
|
+
it { should raise_error(Typed::NotDefined) }
|
134
|
+
it { should raise_error(/xxx is not a member of A/) }
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "write twice" do
|
138
|
+
context "val" do
|
139
|
+
subject { lambda { b = B.new; b.key = 0; b.key = 1 } }
|
140
|
+
it { should raise_error(Typed::FixedValue) }
|
141
|
+
it { should raise_error(/reassignment to key/) }
|
142
|
+
end
|
143
|
+
|
144
|
+
context "var" do
|
145
|
+
subject { lambda { b = B.new; b.url = "x"; b.url = "y" } }
|
146
|
+
it { should_not raise_error }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
######################################################################
|
152
|
+
### Inheritance
|
153
|
+
|
154
|
+
context "(Point3D < Point2D)" do
|
155
|
+
before { source("Point2D", <<-EOF)
|
156
|
+
class Point2D
|
157
|
+
include Typed::Scala
|
158
|
+
val x = Fixnum
|
159
|
+
val y = Fixnum
|
160
|
+
end
|
161
|
+
EOF
|
162
|
+
}
|
163
|
+
|
164
|
+
before { source("Point3D", <<-EOF)
|
165
|
+
class Point3D < Point2D
|
166
|
+
val z = Fixnum
|
167
|
+
end
|
168
|
+
EOF
|
169
|
+
}
|
170
|
+
|
171
|
+
context "Point2D" do
|
172
|
+
specify "contains x,y" do
|
173
|
+
Point2D.vals.keys.should == %w( x y )
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "#x, #x=" do
|
177
|
+
specify "exist" do
|
178
|
+
p = Point2D.new
|
179
|
+
p.x = 1
|
180
|
+
p.x.should == 1
|
181
|
+
lambda { p.x = 2 }.should raise_error(Typed::FixedValue)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "#z" do
|
186
|
+
specify "not exist" do
|
187
|
+
p = Point2D.new
|
188
|
+
lambda { p["z"] = 3 }.should raise_error(Typed::NotDefined)
|
189
|
+
lambda { p.z = 3 }.should raise_error(NoMethodError)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context "Point3D" do
|
195
|
+
specify "contains x,y,z" do
|
196
|
+
Point3D.vals.keys.should == %w( x y z )
|
197
|
+
end
|
198
|
+
|
199
|
+
describe "#x, #x=, [x], [x]=" do
|
200
|
+
specify "exist" do
|
201
|
+
p = Point3D.new
|
202
|
+
p.x = 1
|
203
|
+
p.x.should == 1
|
204
|
+
p["x"].should == 1
|
205
|
+
lambda { p.x = 2 }.should raise_error(Typed::FixedValue)
|
206
|
+
lambda { p["x"] = 2 }.should raise_error(Typed::FixedValue)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe "#z, #z=, [z], [z]=" do
|
211
|
+
specify "exist" do
|
212
|
+
p = Point3D.new
|
213
|
+
p.z = 1
|
214
|
+
p.z.should == 1
|
215
|
+
p["z"].should == 1
|
216
|
+
lambda { p.z = 2 }.should raise_error(Typed::FixedValue)
|
217
|
+
lambda { p["z"] = 2 }.should raise_error(Typed::FixedValue)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
######################################################################
|
224
|
+
### TODO
|
225
|
+
|
226
|
+
describe "check method conflictions like [], []=" do
|
227
|
+
specify do
|
228
|
+
pending "will be implemented in version 0.3.0"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context "when val or var are overridden in same context" do
|
233
|
+
specify do
|
234
|
+
pending "will be implemented in version 0.3.0"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context "when val or var are overridden in subclass" do
|
239
|
+
specify do
|
240
|
+
pending "will be implemented in version 0.3.0"
|
241
|
+
end
|
55
242
|
end
|
56
243
|
end
|
data/spec/schema_spec.rb
CHANGED
@@ -1,6 +1,24 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Typed::Schema do
|
4
|
+
describe "#exist?" do
|
5
|
+
let(:hash) { Typed::Hash.new }
|
6
|
+
let(:schema) { hash.schema }
|
7
|
+
|
8
|
+
context "(not exist)" do
|
9
|
+
specify "false" do
|
10
|
+
schema.exist?(:a).should == false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "(exist)" do
|
15
|
+
before { hash[:a] = String }
|
16
|
+
specify "true" do
|
17
|
+
schema.exist?(:a).should == true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
4
22
|
describe ".schema?" do
|
5
23
|
delegate :schema?, :to => "Typed::Schema"
|
6
24
|
|
data/spec/spec_helper.rb
CHANGED
@@ -3,6 +3,22 @@ $:.unshift File.expand_path('../../lib', __FILE__)
|
|
3
3
|
require 'rspec'
|
4
4
|
require 'typed'
|
5
5
|
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
# == Mock Framework
|
9
|
+
#
|
10
|
+
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
11
|
+
#
|
12
|
+
# config.mock_with :mocha
|
13
|
+
# config.mock_with :flexmock
|
14
|
+
# config.mock_with :rr
|
15
|
+
config.mock_with :rspec
|
16
|
+
|
17
|
+
config.filter_run :focus => true
|
18
|
+
config.run_all_when_everything_filtered = true
|
19
|
+
end
|
20
|
+
|
21
|
+
|
6
22
|
def tmp_path(file)
|
7
23
|
Pathname(File.dirname(__FILE__)) + "../tmp" + file
|
8
24
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 7
|
10
|
+
version: 0.2.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- maiha
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2013-08-
|
18
|
+
date: 2013-08-28 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: activesupport
|