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