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 CHANGED
@@ -3,3 +3,5 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  tmp
6
+ vendor
7
+ .ruby-version
@@ -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
@@ -5,6 +5,7 @@ require "typed/hash"
5
5
 
6
6
  module Typed
7
7
  NotDefined = Class.new(RuntimeError)
8
+ FixedValue = Class.new(RuntimeError)
8
9
 
9
10
  autoload :Schema , "typed/schema"
10
11
  autoload :Default, "typed/default"
@@ -1,59 +1,122 @@
1
1
  module Typed
2
2
  module Scala
3
- def vars
4
- unless @vars
5
- vars = Typed::Hash.new
6
- self.class.types.each_pair do |name, obj|
7
- # vars.default[name] = obj
8
- vars[name] = obj
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
- @vars = vars
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
- def types
16
- self.class.types
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
- def types
22
- @types ||= {}
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 register_var(name, obj)
26
- types[name] = obj
64
+ def self.define(klass, type, name, obj)
65
+ vars = klass.__send__("#{type}s")
66
+ vars[name] = obj
27
67
 
28
- define_method(name) { vars[name.to_s] }
29
- define_method("#{name}=") {|v| vars[name.to_s] = v }
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 parse_var_name(caller)
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 = $1
37
- line = $2.to_i
38
- @lines ||= File.readlines(file)
39
- case @lines[line-1].to_s
40
- when /^\s*var\s+(\S+)\s+=/o
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 file:#{file} (line: #{line})"
86
+ raise ParseError, "#{self} from #{caller}"
44
87
  end
45
88
  else
46
- raise ParseError, "#{self} from caller:#{caller[0]}"
89
+ raise ParseError, "#{self} from caller:#{caller}"
47
90
  end
48
91
  end
49
92
 
50
- def var(obj)
51
- name = parse_var_name(caller[0])
52
- register_var(name, obj)
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
@@ -45,6 +45,10 @@ module Typed
45
45
  @types[key.to_s].value
46
46
  end
47
47
 
48
+ def exist?(key)
49
+ @types.key?(key.to_s)
50
+ end
51
+
48
52
  def declare!(key, declare)
49
53
  case declare.must.be.kind_of(Explicit, Implicit)
50
54
  when Explicit
@@ -1,3 +1,3 @@
1
1
  module Typed
2
- VERSION = "0.2.6"
2
+ VERSION = "0.2.7"
3
3
  end
@@ -1,56 +1,243 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Typed::Scala do
4
- before { write_and_load(source) }
5
-
6
- let(:source) { commented_source.gsub(/^\s*#/m, '') }
7
- let(:user) { User.new }
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
- context "User" do
18
- # comment outed for the editor issues
19
- let(:commented_source) { <<-EOF
20
- # class User
21
- # include Typed::Scala
22
- #
23
- # var name = String
24
- # var age = Fixnum
25
- # end
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
- it { should respond_to(:name) }
30
- it { should respond_to(:age) }
31
- it { should_not respond_to(:xxx) }
50
+ describe "#name=" do
51
+ specify "accept 'maiha'" do
52
+ (user.name = 'maiha').should == 'maiha'
53
+ end
32
54
 
33
- describe "#types" do
34
- its(:types) { should == {
35
- "age" => Fixnum,
36
- "name" => String,
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
- describe ".types" do
41
- specify "same as User#type" do
42
- user.types.should == User.types
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
- describe "#name=" do
47
- specify "accept 'maiha'" do
48
- (user.name = 'maiha').should == 'maiha'
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
- specify "reject 100" do
52
- lambda { user.name = 100 }.should raise_error(TypeError)
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
@@ -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
 
@@ -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: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 6
10
- version: 0.2.6
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-26 00:00:00 Z
18
+ date: 2013-08-28 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activesupport