classx 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/ChangeLog +354 -0
  2. data/README +5 -0
  3. data/Rakefile +2 -2
  4. data/bench/attribute_get.rb +73 -0
  5. data/bench/attribute_set.rb +53 -19
  6. data/bench/define_attribute.rb +226 -0
  7. data/bench/initialize.rb +25 -7
  8. data/doc/output/coverage/index.html +74 -128
  9. data/doc/output/coverage/lib-classx-attribute_rb.html +291 -190
  10. data/doc/output/coverage/lib-classx-attributes_rb.html +167 -101
  11. data/doc/output/coverage/lib-classx-bracketable_rb.html +671 -0
  12. data/doc/output/coverage/lib-classx-class_attributes_rb.html +775 -0
  13. data/doc/output/coverage/lib-classx-commandable_rb.html +727 -0
  14. 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
  15. data/doc/output/coverage/lib-classx-validate_rb.html +43 -41
  16. data/doc/output/coverage/lib-classx_rb.html +208 -78
  17. data/example/commandable.rb +1 -0
  18. data/lib/classx.rb +146 -16
  19. data/lib/classx/attribute.rb +244 -143
  20. data/lib/classx/attributes.rb +93 -27
  21. data/lib/classx/bracketable.rb +61 -0
  22. data/lib/classx/class_attributes.rb +165 -0
  23. data/lib/classx/commandable.rb +55 -4
  24. data/lib/classx/declare.rb +40 -3
  25. data/lib/classx/role/logger.rb +17 -13
  26. data/lib/classx/validate.rb +26 -24
  27. data/spec/classx/handles_spec.rb +21 -6
  28. data/spec/classx/serialize_spec.rb +57 -0
  29. data/spec/classx/{with_coerce.rb → with_coerce_spec.rb} +0 -0
  30. data/spec/classx/with_extend_spec.rb +58 -0
  31. data/spec/classx/with_include_spec.rb +58 -0
  32. data/spec/classx/with_validate_spec.rb +363 -0
  33. data/spec/classx/without_anyoption_spec.rb +1 -1
  34. data/spec/classx/writable_option_spec.rb +29 -4
  35. data/spec/classx_bracketable_spec.rb +160 -0
  36. data/spec/classx_class_attributes/default_option_spec.rb +121 -0
  37. data/spec/classx_class_attributes/dsl_accessor_spec.rb +88 -0
  38. data/spec/classx_class_attributes/handles_spec.rb +77 -0
  39. data/spec/classx_class_attributes/with_coerce_spec.rb +119 -0
  40. data/spec/classx_class_attributes/with_extend_spec.rb +64 -0
  41. data/spec/classx_class_attributes/with_include_spec.rb +64 -0
  42. data/spec/classx_class_attributes/with_multiple_class_spec.rb +60 -0
  43. data/spec/classx_class_attributes/with_validate_spec.rb +248 -0
  44. data/spec/classx_class_attributes/without_accessor_spec.rb +19 -0
  45. data/spec/classx_class_attributes/without_anyoption_spec.rb +21 -0
  46. data/spec/classx_class_attributes/writable_option_spec.rb +100 -0
  47. data/spec/classx_declare_spec.rb +117 -0
  48. data/spec/classx_validate_spec.rb +1 -3
  49. data/tasks/basic_tasks.rake +1 -1
  50. metadata +36 -26
  51. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-callbacks_rb.html +0 -932
  52. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-change_rb.html +0 -779
  53. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-hunk_rb.html +0 -867
  54. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs_rb.html +0 -1715
  55. data/doc/output/coverage/-Library-Ruby-Gems-gems-rcov-0_8_1_2_0-lib-rcov_rb.html +0 -1598
  56. data/spec/classx/with_extend.rb +0 -27
  57. data/spec/classx/with_include.rb +0 -27
@@ -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
- def define_attribute name, attribute
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 = Module.new
21
- begin
51
+ mod = nil
52
+ if self.const_defined? 'ClassMethods'
22
53
  mod = self.const_get('ClassMethods')
23
- rescue NameError => e
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
- def add_attribute name, attrs={}
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
- define_method name do
52
- attribute_of[name].get
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[name].set val
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
- cached_attribute_of = attribute_of
60
- if cached_attribute_of[name]
61
- unless cached_attribute_of[name].config[:writable]
62
- private "#{name}="
63
- end
126
+ unless attr_class.config[:writable]
127
+ private "#{name}="
128
+ end
64
129
 
65
- if cached_attribute_of[name].config[:handles]
66
- case cached_attribute_of[name].config[:handles]
67
- when Hash
68
- cached_attribute_of[name].config[:handles].each do |before, after|
69
- define_method before do
70
- attribute_of[name].get.__send__ after
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
- when Array
74
- cached_attribute_of[name].config[:handles].each do |meth|
75
- define_method meth do
76
- attribute_of[name].get.__send__ meth
77
- end
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
@@ -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("-#{short_option}", "--#{key} #{val_format}", val.value_class, val.desc) {|v| value_of[key] = v }
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("-#{short_option}", "--#{key} #{$ClassXCommandableMappingOf[val.value_class]}", $ClassXCommandableMappingOf[val.value_class], val.desc) {|v| value_of[key] = v }
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("--#{key} #{$ClassXCommandableMappingOf[val.value_class]}", $ClassXCommandableMappingOf[val.value_class], val.desc) {|v| value_of[key] = v }
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::AttrRequiredError, OptionParser::ParseError => e
110
+ rescue ClassX::InstanceException, OptionParser::ParseError => e
60
111
  warn opt
61
112
  exit
62
113
  end