typed 0.2.6 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|