classx 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +354 -0
- data/README +5 -0
- data/Rakefile +2 -2
- data/bench/attribute_get.rb +73 -0
- data/bench/attribute_set.rb +53 -19
- data/bench/define_attribute.rb +226 -0
- data/bench/initialize.rb +25 -7
- data/doc/output/coverage/index.html +74 -128
- data/doc/output/coverage/lib-classx-attribute_rb.html +291 -190
- data/doc/output/coverage/lib-classx-attributes_rb.html +167 -101
- data/doc/output/coverage/lib-classx-bracketable_rb.html +671 -0
- data/doc/output/coverage/lib-classx-class_attributes_rb.html +775 -0
- data/doc/output/coverage/lib-classx-commandable_rb.html +727 -0
- data/doc/output/coverage/{-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-block_rb.html → lib-classx-declare_rb.html} +60 -62
- data/doc/output/coverage/lib-classx-validate_rb.html +43 -41
- data/doc/output/coverage/lib-classx_rb.html +208 -78
- data/example/commandable.rb +1 -0
- data/lib/classx.rb +146 -16
- data/lib/classx/attribute.rb +244 -143
- data/lib/classx/attributes.rb +93 -27
- data/lib/classx/bracketable.rb +61 -0
- data/lib/classx/class_attributes.rb +165 -0
- data/lib/classx/commandable.rb +55 -4
- data/lib/classx/declare.rb +40 -3
- data/lib/classx/role/logger.rb +17 -13
- data/lib/classx/validate.rb +26 -24
- data/spec/classx/handles_spec.rb +21 -6
- data/spec/classx/serialize_spec.rb +57 -0
- data/spec/classx/{with_coerce.rb → with_coerce_spec.rb} +0 -0
- data/spec/classx/with_extend_spec.rb +58 -0
- data/spec/classx/with_include_spec.rb +58 -0
- data/spec/classx/with_validate_spec.rb +363 -0
- data/spec/classx/without_anyoption_spec.rb +1 -1
- data/spec/classx/writable_option_spec.rb +29 -4
- data/spec/classx_bracketable_spec.rb +160 -0
- data/spec/classx_class_attributes/default_option_spec.rb +121 -0
- data/spec/classx_class_attributes/dsl_accessor_spec.rb +88 -0
- data/spec/classx_class_attributes/handles_spec.rb +77 -0
- data/spec/classx_class_attributes/with_coerce_spec.rb +119 -0
- data/spec/classx_class_attributes/with_extend_spec.rb +64 -0
- data/spec/classx_class_attributes/with_include_spec.rb +64 -0
- data/spec/classx_class_attributes/with_multiple_class_spec.rb +60 -0
- data/spec/classx_class_attributes/with_validate_spec.rb +248 -0
- data/spec/classx_class_attributes/without_accessor_spec.rb +19 -0
- data/spec/classx_class_attributes/without_anyoption_spec.rb +21 -0
- data/spec/classx_class_attributes/writable_option_spec.rb +100 -0
- data/spec/classx_declare_spec.rb +117 -0
- data/spec/classx_validate_spec.rb +1 -3
- data/tasks/basic_tasks.rake +1 -1
- metadata +36 -26
- data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-callbacks_rb.html +0 -932
- data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-change_rb.html +0 -779
- data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-hunk_rb.html +0 -867
- data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs_rb.html +0 -1715
- data/doc/output/coverage/-Library-Ruby-Gems-gems-rcov-0_8_1_2_0-lib-rcov_rb.html +0 -1598
- data/spec/classx/with_extend.rb +0 -27
- data/spec/classx/with_include.rb +0 -27
data/lib/classx/attributes.rb
CHANGED
@@ -1,9 +1,38 @@
|
|
1
1
|
module ClassX
|
2
|
+
#
|
3
|
+
# added attribute feature to module.
|
4
|
+
#
|
5
|
+
# require 'classx'
|
6
|
+
# module YourApp::Role::SomeModule
|
7
|
+
# extend Attributes
|
8
|
+
#
|
9
|
+
# has :some_attr
|
10
|
+
#
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# class YourApp
|
14
|
+
# included ClassX
|
15
|
+
# included YourApp::Role::SomeModule
|
16
|
+
#
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# YourApp.new(:some_attr => 10)
|
20
|
+
#
|
2
21
|
module Attributes
|
3
22
|
ATTRIBUTE_REGEX = /\Aattribute_of:(\w+)\z/
|
4
23
|
|
24
|
+
# return Hash of attribute's name as a key and attribute's meta class as a value.
|
25
|
+
# for example,
|
26
|
+
#
|
27
|
+
# class YourClass
|
28
|
+
# include ClassX
|
29
|
+
# has :x
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# YourClass.attribute_of #=> { "x" => <ClassX::Attribute: ... > }
|
33
|
+
#
|
5
34
|
def attribute_of
|
6
|
-
unless @__attribute_of
|
35
|
+
unless instance_variable_defined?('@__attribute_of') && @__attribute_of
|
7
36
|
@__attribute_of = {}
|
8
37
|
private_instance_methods.select {|meth| meth.to_s =~ ATTRIBUTE_REGEX }.each do |meth|
|
9
38
|
key = meth.to_s.sub(ATTRIBUTE_REGEX) { $1 }
|
@@ -15,12 +44,14 @@ module ClassX
|
|
15
44
|
end
|
16
45
|
|
17
46
|
private
|
18
|
-
|
47
|
+
# generating attribute's meta class and bind to this class.
|
48
|
+
#
|
49
|
+
def define_attribute name, attribute #:doc:
|
19
50
|
klass_attribute = ClassX::AttributeFactory.create(attribute)
|
20
|
-
mod =
|
21
|
-
|
51
|
+
mod = nil
|
52
|
+
if self.const_defined? 'ClassMethods'
|
22
53
|
mod = self.const_get('ClassMethods')
|
23
|
-
|
54
|
+
else
|
24
55
|
mod = Module.new
|
25
56
|
const_set('ClassMethods', mod)
|
26
57
|
end
|
@@ -41,40 +72,73 @@ module ClassX
|
|
41
72
|
end
|
42
73
|
|
43
74
|
private "attribute_of:#{name}"
|
75
|
+
|
76
|
+
klass_attribute
|
44
77
|
end
|
45
78
|
|
46
|
-
|
79
|
+
# adding new attribute to class, and define accessor methods related to this attribute.
|
80
|
+
# You can also use +has+ more declaretivly.
|
81
|
+
#
|
82
|
+
# class YourClass
|
83
|
+
# include ClassX
|
84
|
+
# add_attribute :x,
|
85
|
+
# :writable => true, # defining accessor scope.
|
86
|
+
# :optional => ture, # defining attribute is required in initialize.
|
87
|
+
# :validate => proc {|val| val.respond_to? :to_s } # this attribute's value should adopted to this rule.
|
88
|
+
# :default => proc {|mine| mine.class.to_s.split(/::/).last.downcase } # default value for this attribute.
|
89
|
+
#
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
def add_attribute name, attrs={} #:doc:
|
47
93
|
name = name.to_s
|
48
94
|
|
49
|
-
define_attribute(name, attrs)
|
95
|
+
attr_class = define_attribute(name, attrs)
|
50
96
|
|
51
|
-
|
52
|
-
|
97
|
+
# XXX: Why this can take *args?
|
98
|
+
# => It's for avoid warnings when you call it without values.
|
99
|
+
define_method name do |*vals|
|
100
|
+
if vals == []
|
101
|
+
@__attribute_data_of ||= {}
|
102
|
+
if @__attribute_data_of[name]
|
103
|
+
return @__attribute_data_of[name]
|
104
|
+
else
|
105
|
+
attr_instance = __send__ "attribute_of:#{name}"
|
106
|
+
return @__attribute_data_of[name] = attr_instance.get
|
107
|
+
end
|
108
|
+
else
|
109
|
+
raise ArgumentError if vals.size > 1
|
110
|
+
val = vals.first
|
111
|
+
if respond_to? "#{name}="
|
112
|
+
__send__ "#{name}=", val
|
113
|
+
else
|
114
|
+
raise RuntimeError, ":#{name} is not writable"
|
115
|
+
end
|
116
|
+
end
|
53
117
|
end
|
54
118
|
|
55
119
|
define_method "#{name}=" do |val|
|
56
|
-
attribute_of
|
120
|
+
attr_instance = __send__ "attribute_of:#{name}"
|
121
|
+
attr_instance.set val
|
122
|
+
@__attribute_data_of ||= {}
|
123
|
+
@__attribute_data_of[name] = val
|
57
124
|
end
|
58
125
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
private "#{name}="
|
63
|
-
end
|
126
|
+
unless attr_class.config[:writable]
|
127
|
+
private "#{name}="
|
128
|
+
end
|
64
129
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
130
|
+
if attr_class.config[:handles]
|
131
|
+
case attr_class.config[:handles]
|
132
|
+
when Hash
|
133
|
+
attr_class.config[:handles].each do |before, after|
|
134
|
+
define_method before do |*args|
|
135
|
+
__send__("#{name}").__send__ after, *args
|
72
136
|
end
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
137
|
+
end
|
138
|
+
when Array
|
139
|
+
attr_class.config[:handles].each do |meth|
|
140
|
+
define_method meth do |*args|
|
141
|
+
__send__("#{name}").__send__ meth, *args
|
78
142
|
end
|
79
143
|
end
|
80
144
|
end
|
@@ -88,5 +152,7 @@ module ClassX
|
|
88
152
|
klass.extend(self.const_get('ClassMethods'))
|
89
153
|
end
|
90
154
|
|
155
|
+
# alias for lazy people
|
156
|
+
Attrs = Attributes
|
91
157
|
end
|
92
158
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ClassX
|
2
|
+
# you can access attribute using Hash like interface.
|
3
|
+
# class YourClass
|
4
|
+
# include ClassX
|
5
|
+
# include ClassX::Bracketable
|
6
|
+
# has :x
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# your = YourClass.new(:x => 20)
|
10
|
+
# your[:x] #=> 20
|
11
|
+
# your[:x] = 30
|
12
|
+
# your[:x] #=> 30
|
13
|
+
#
|
14
|
+
# you can also use for class attribute
|
15
|
+
#
|
16
|
+
# class YourClass
|
17
|
+
# extend ClssX::CAttrs
|
18
|
+
# extend ClassX::Bracketable
|
19
|
+
#
|
20
|
+
# class_has :x
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# YourClass[:x] = 20
|
24
|
+
# YourClass[:x] #=> 20
|
25
|
+
#
|
26
|
+
module Bracketable
|
27
|
+
def [] name
|
28
|
+
attr_meth = nil
|
29
|
+
if respond_to? :class_attribute_of, true
|
30
|
+
attr_meth = :class_attribute_of
|
31
|
+
elsif respond_to? :attribute_of, true
|
32
|
+
attr_meth = :attribute_of
|
33
|
+
else
|
34
|
+
return nil
|
35
|
+
end
|
36
|
+
|
37
|
+
if __send__(attr_meth).keys.include? name.to_s
|
38
|
+
__send__ name
|
39
|
+
else
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def []= name, val
|
45
|
+
attr_meth = nil
|
46
|
+
if respond_to? :class_attribute_of, true
|
47
|
+
attr_meth = :class_attribute_of
|
48
|
+
elsif respond_to? :attribute_of, true
|
49
|
+
attr_meth = :attribute_of
|
50
|
+
else
|
51
|
+
return nil
|
52
|
+
end
|
53
|
+
|
54
|
+
if __send__(attr_meth).keys.include?(name.to_s) && respond_to?("#{name.to_s}=")
|
55
|
+
__send__ "#{name.to_s}=", val
|
56
|
+
else
|
57
|
+
raise NoMethodError, ":#{name} is private, or missing"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module ClassX
|
4
|
+
#
|
5
|
+
# for easy defining inheritable class accessor using classx attribute interface.
|
6
|
+
#
|
7
|
+
# require 'classx'
|
8
|
+
# class YourClass
|
9
|
+
# extend ClassX::ClassAttributes
|
10
|
+
#
|
11
|
+
# class_has :table_name, :writable => true, :default => proc {|klass| klass.to_s.downcase }
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# YourClass.table_name #=> 'yourclass'
|
15
|
+
# YourClass.table_name = 'test'
|
16
|
+
# YourClass.table_name #=> 'test'
|
17
|
+
#
|
18
|
+
# class SubClass < YourClass
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# SubClass.table_name #=> 'subclass' # it's not "test"!!
|
22
|
+
#
|
23
|
+
# # you can also write following:
|
24
|
+
# SubClass.table_name('test2')
|
25
|
+
# SubClass.table_name #=> 'test2'
|
26
|
+
#
|
27
|
+
#
|
28
|
+
# == SEE ALSO
|
29
|
+
# dsl_accessor:
|
30
|
+
# It's have also similar functions. It's implemented simply using activesupport's inheritable_accessor.
|
31
|
+
# classx is complex but more extensible to define class attribute.
|
32
|
+
#
|
33
|
+
module ClassAttributes
|
34
|
+
CLASS_ATTRIBUTE_REGEX = /\Aclass_attribute_of:(\w+)\z/
|
35
|
+
|
36
|
+
def class_attribute_of
|
37
|
+
unless instance_variable_defined?('@__class_attribute_of') && @__class_attribute_of
|
38
|
+
@__class_attribute_of = {}
|
39
|
+
private_methods.select {|meth| meth.to_s =~ CLASS_ATTRIBUTE_REGEX }.each do |meth|
|
40
|
+
key = meth.to_s.sub(CLASS_ATTRIBUTE_REGEX) { $1 }
|
41
|
+
@__class_attribute_of[key] = __send__("class_attribute_of:#{key}").new(self)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
@__class_attribute_of
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def define_class_attribute name, attribute
|
50
|
+
klass_attribute = ClassX::AttributeFactory.create(attribute)
|
51
|
+
mod = nil
|
52
|
+
if self.const_defined? 'ClassMethods'
|
53
|
+
mod = self.const_get('ClassMethods')
|
54
|
+
else
|
55
|
+
mod = Module.new
|
56
|
+
const_set('ClassMethods', mod)
|
57
|
+
end
|
58
|
+
mod.module_eval do
|
59
|
+
define_method "class_attribute_of:#{name}" do
|
60
|
+
klass_attribute
|
61
|
+
end
|
62
|
+
|
63
|
+
private "class_attribute_of:#{name}"
|
64
|
+
end
|
65
|
+
self.extend(mod)
|
66
|
+
@__class_attribute_of ||= self.class_attribute_of
|
67
|
+
@__class_attribute_of[name] = klass_attribute.new(self)
|
68
|
+
|
69
|
+
klass_attribute
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_class_attribute name, attrs={}
|
73
|
+
name = name.to_s
|
74
|
+
|
75
|
+
attr_class = define_class_attribute(name, attrs)
|
76
|
+
|
77
|
+
mod = nil
|
78
|
+
if self.const_defined? 'ClassMethods'
|
79
|
+
mod = self.const_get('ClassMethods')
|
80
|
+
else
|
81
|
+
mod = Module.new
|
82
|
+
const_set('ClassMethods', mod)
|
83
|
+
end
|
84
|
+
mod.module_eval do
|
85
|
+
# XXX: Why this can take *args?
|
86
|
+
# => It's for avoid warnings when you call it without values.
|
87
|
+
define_method name do |*vals|
|
88
|
+
name = name.to_s
|
89
|
+
if vals == []
|
90
|
+
@__class_attribute_data_of ||= {}
|
91
|
+
if @__class_attribute_data_of[name]
|
92
|
+
return @__class_attribute_data_of[name]
|
93
|
+
else
|
94
|
+
attr_instance = nil
|
95
|
+
if instance_variable_defined?('@__class_attribute_of') && @__class_attribute_of
|
96
|
+
attr_instance = @__class_attribute_of[name]
|
97
|
+
else
|
98
|
+
@__class_attribute_of = self.class_attribute_of
|
99
|
+
attr_instance = @__class_attribute_of[name]
|
100
|
+
end
|
101
|
+
result = attr_instance.get
|
102
|
+
raise ClassX::AttrRequiredError if result.nil? && !attr_instance.class.config[:optional]
|
103
|
+
return @__class_attribute_data_of[name] = result
|
104
|
+
end
|
105
|
+
else
|
106
|
+
raise ArgumentError if vals.size > 1
|
107
|
+
val = vals.first
|
108
|
+
# TODO: It's not consider whether setter method is writable.
|
109
|
+
# I want to write following when setter method is private:
|
110
|
+
#
|
111
|
+
# class SomeClass
|
112
|
+
# class_has :attr
|
113
|
+
# attr 'setting value example'
|
114
|
+
# self.attr = 'stting value example' # It's same meaning above but longer and not declaretive.
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
__send__ "#{name}=", val
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
define_method "#{name}=" do |val|
|
122
|
+
nam = name.to_s
|
123
|
+
attr_instance = nil
|
124
|
+
if @__class_attribute_of
|
125
|
+
attr_instance = @__class_attribute_of[name]
|
126
|
+
else
|
127
|
+
@__class_attribute_of = self.class_attribute_of
|
128
|
+
attr_instance = @__class_attribute_of[name]
|
129
|
+
end
|
130
|
+
|
131
|
+
attr_instance.set val
|
132
|
+
@__class_attribute_data_of ||= {}
|
133
|
+
@__class_attribute_data_of[name] = val
|
134
|
+
end
|
135
|
+
|
136
|
+
unless attr_class.config[:writable]
|
137
|
+
private "#{name}="
|
138
|
+
end
|
139
|
+
|
140
|
+
if attr_class.config[:handles]
|
141
|
+
case attr_class.config[:handles]
|
142
|
+
when Hash
|
143
|
+
attr_class.config[:handles].each do |before, after|
|
144
|
+
define_method before do |*args|
|
145
|
+
__send__("#{name}").__send__ after, *args
|
146
|
+
end
|
147
|
+
end
|
148
|
+
when Array
|
149
|
+
attr_class.config[:handles].each do |meth|
|
150
|
+
define_method meth do |*args|
|
151
|
+
__send__("#{name}").__send__ meth, *args
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
self.extend(mod)
|
158
|
+
end
|
159
|
+
|
160
|
+
alias class_has add_class_attribute
|
161
|
+
end
|
162
|
+
|
163
|
+
# alias for lazy people.
|
164
|
+
CAttrs = ClassAttributes
|
165
|
+
end
|
data/lib/classx/commandable.rb
CHANGED
@@ -3,6 +3,45 @@ require 'optparse'
|
|
3
3
|
$ClassXCommandableMappingOf = {}
|
4
4
|
|
5
5
|
module ClassX
|
6
|
+
# add cli interface to your classx based class.
|
7
|
+
#
|
8
|
+
# require 'classx'
|
9
|
+
#
|
10
|
+
# $ClassXCommandableMappingOf[Symbol] = String
|
11
|
+
#
|
12
|
+
# class YourApp
|
13
|
+
# include ClassX
|
14
|
+
# extend ClassX::Commandable
|
15
|
+
#
|
16
|
+
# has :arg1,
|
17
|
+
# :kind_of => Symbol,
|
18
|
+
# :desc => 'please specify arg1',
|
19
|
+
# :coerce => { String => proc {|val| val.to_sym } }
|
20
|
+
#
|
21
|
+
# has :arg2,
|
22
|
+
# :kind_of => Integer,
|
23
|
+
# :desc => "this is arg2",
|
24
|
+
# :optional => true
|
25
|
+
#
|
26
|
+
# def run
|
27
|
+
# # do something!!
|
28
|
+
# p attribute_of
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# if $0 == __FILE__
|
33
|
+
# YourApp.from_argv.run
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# and run, $ruby example/commandable.rb
|
37
|
+
#
|
38
|
+
# example/commandable.rb [options]
|
39
|
+
# -a, --arg1 String please specify arg1
|
40
|
+
# --arg2 [Integer] this is arg2
|
41
|
+
# -h, --help show this document
|
42
|
+
#
|
43
|
+
# please see and run example/commandable.rb
|
44
|
+
#
|
6
45
|
module Commandable
|
7
46
|
class MissingCoerceMapping < Exception; end
|
8
47
|
|
@@ -29,16 +68,27 @@ module ClassX
|
|
29
68
|
if val.value_class
|
30
69
|
begin
|
31
70
|
if short_option_of[short_option] == key
|
32
|
-
opt.on(
|
71
|
+
opt.on(
|
72
|
+
"-#{short_option}",
|
73
|
+
"--#{key} #{val_format}",
|
74
|
+
val.value_class, val.desc
|
75
|
+
) {|v| value_of[key] = v }
|
33
76
|
else
|
34
77
|
opt.on("--#{key} #{val_format}", val.value_class, val.desc) {|v| value_of[key] = v }
|
35
78
|
end
|
36
79
|
rescue Exception => e
|
37
80
|
if $ClassXCommandableMappingOf[val.value_class]
|
38
81
|
if short_option_of[short_option] == key
|
39
|
-
opt.on(
|
82
|
+
opt.on(
|
83
|
+
"-#{short_option}",
|
84
|
+
"--#{key} #{$ClassXCommandableMappingOf[val.value_class]}",
|
85
|
+
$ClassXCommandableMappingOf[val.value_class], val.desc
|
86
|
+
) {|v| value_of[key] = v }
|
40
87
|
else
|
41
|
-
opt.on(
|
88
|
+
opt.on(
|
89
|
+
"--#{key} #{$ClassXCommandableMappingOf[val.value_class]}",
|
90
|
+
$ClassXCommandableMappingOf[val.value_class], val.desc
|
91
|
+
) {|v| value_of[key] = v }
|
42
92
|
end
|
43
93
|
else
|
44
94
|
raise MissingCoerceMapping, "missing coerce rule. please specify $ClassXCommandableMappingOf"
|
@@ -55,8 +105,9 @@ module ClassX
|
|
55
105
|
|
56
106
|
opt.on('-h', '--help', 'show this document') {|v| raise OptionParser::ParseError }
|
57
107
|
opt.parse!(argv)
|
108
|
+
|
58
109
|
return new(value_of)
|
59
|
-
rescue ClassX::
|
110
|
+
rescue ClassX::InstanceException, OptionParser::ParseError => e
|
60
111
|
warn opt
|
61
112
|
exit
|
62
113
|
end
|