classx 0.0.3 → 0.0.4
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/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
|