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 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