ant-mapper 0.0.2

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 (52) hide show
  1. data/CHANGE +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +25 -0
  4. data/ant.gemspec +48 -0
  5. data/data.tch +0 -0
  6. data/examples/account.rb +71 -0
  7. data/examples/data.tch +0 -0
  8. data/examples/light_cloud.yml +18 -0
  9. data/examples/user.rb +84 -0
  10. data/init.rb +4 -0
  11. data/lib/ant.rb +7 -0
  12. data/lib/ant_mapper.rb +10 -0
  13. data/lib/ant_mapper/adapters/light_cloud.rb +59 -0
  14. data/lib/ant_mapper/adapters/tokyo_cabinet.rb +42 -0
  15. data/lib/ant_mapper/adapters/tokyo_tyrant.rb +14 -0
  16. data/lib/ant_mapper/base.rb +367 -0
  17. data/lib/ant_mapper/callbacks.rb +180 -0
  18. data/lib/ant_mapper/observer.rb +180 -0
  19. data/lib/ant_mapper/validations.rb +687 -0
  20. data/lib/ant_support.rb +4 -0
  21. data/lib/ant_support/callbacks.rb +303 -0
  22. data/lib/ant_support/core_ext.rb +4 -0
  23. data/lib/ant_support/core_ext/array.rb +5 -0
  24. data/lib/ant_support/core_ext/array/extract_options.rb +20 -0
  25. data/lib/ant_support/core_ext/blank.rb +58 -0
  26. data/lib/ant_support/core_ext/class.rb +3 -0
  27. data/lib/ant_support/core_ext/class/attribute_accessors.rb +54 -0
  28. data/lib/ant_support/core_ext/class/inheritable_attributes.rb +140 -0
  29. data/lib/ant_support/core_ext/class/removal.rb +50 -0
  30. data/lib/ant_support/core_ext/duplicable.rb +43 -0
  31. data/lib/ant_support/core_ext/enumerable.rb +72 -0
  32. data/lib/ant_support/core_ext/hash.rb +6 -0
  33. data/lib/ant_support/core_ext/hash/keys.rb +52 -0
  34. data/lib/ant_support/core_ext/module.rb +16 -0
  35. data/lib/ant_support/core_ext/module/aliasing.rb +74 -0
  36. data/lib/ant_support/core_ext/module/attr_accessor_with_default.rb +31 -0
  37. data/lib/ant_support/core_ext/module/attribute_accessors.rb +58 -0
  38. data/lib/ant_support/core_ext/object.rb +1 -0
  39. data/lib/ant_support/core_ext/object/extending.rb +80 -0
  40. data/lib/ant_support/core_ext/string.rb +7 -0
  41. data/lib/ant_support/core_ext/string/inflections.rb +51 -0
  42. data/spec/case/callbacks_observers_test.rb +38 -0
  43. data/spec/case/callbacks_test.rb +417 -0
  44. data/spec/case/create_object_test.rb +56 -0
  45. data/spec/case/set_class_name_test.rb +17 -0
  46. data/spec/case/validations_test.rb +1482 -0
  47. data/spec/helper.rb +15 -0
  48. data/spec/light_cloud.yml +18 -0
  49. data/spec/model/account.rb +3 -0
  50. data/spec/model/topic.rb +28 -0
  51. data/spec/model/user.rb +4 -0
  52. metadata +125 -0
@@ -0,0 +1,140 @@
1
+ # Retain for backward compatibility. Methods are now included in Class.
2
+ module ClassInheritableAttributes # :nodoc:
3
+ end
4
+
5
+ # Allows attributes to be shared within an inheritance hierarchy, but where each descendant gets a copy of
6
+ # their parents' attributes, instead of just a pointer to the same. This means that the child can add elements
7
+ # to, for example, an array without those additions being shared with either their parent, siblings, or
8
+ # children, which is unlike the regular class-level attributes that are shared across the entire hierarchy.
9
+ class Class # :nodoc:
10
+ def class_inheritable_reader(*syms)
11
+ syms.each do |sym|
12
+ next if sym.is_a?(Hash)
13
+ class_eval <<-EOS
14
+ def self.#{sym}
15
+ read_inheritable_attribute(:#{sym})
16
+ end
17
+
18
+ def #{sym}
19
+ self.class.#{sym}
20
+ end
21
+ EOS
22
+ end
23
+ end
24
+
25
+ def class_inheritable_writer(*syms)
26
+ options = syms.extract_options!
27
+ syms.each do |sym|
28
+ class_eval <<-EOS
29
+ def self.#{sym}=(obj)
30
+ write_inheritable_attribute(:#{sym}, obj)
31
+ end
32
+
33
+ #{"
34
+ def #{sym}=(obj)
35
+ self.class.#{sym} = obj
36
+ end
37
+ " unless options[:instance_writer] == false }
38
+ EOS
39
+ end
40
+ end
41
+
42
+ def class_inheritable_array_writer(*syms)
43
+ options = syms.extract_options!
44
+ syms.each do |sym|
45
+ class_eval <<-EOS
46
+ def self.#{sym}=(obj)
47
+ write_inheritable_array(:#{sym}, obj)
48
+ end
49
+
50
+ #{"
51
+ def #{sym}=(obj)
52
+ self.class.#{sym} = obj
53
+ end
54
+ " unless options[:instance_writer] == false }
55
+ EOS
56
+ end
57
+ end
58
+
59
+ def class_inheritable_hash_writer(*syms)
60
+ options = syms.extract_options!
61
+ syms.each do |sym|
62
+ class_eval <<-EOS
63
+ def self.#{sym}=(obj)
64
+ write_inheritable_hash(:#{sym}, obj)
65
+ end
66
+
67
+ #{"
68
+ def #{sym}=(obj)
69
+ self.class.#{sym} = obj
70
+ end
71
+ " unless options[:instance_writer] == false }
72
+ EOS
73
+ end
74
+ end
75
+
76
+ def class_inheritable_accessor(*syms)
77
+ class_inheritable_reader(*syms)
78
+ class_inheritable_writer(*syms)
79
+ end
80
+
81
+ def class_inheritable_array(*syms)
82
+ class_inheritable_reader(*syms)
83
+ class_inheritable_array_writer(*syms)
84
+ end
85
+
86
+ def class_inheritable_hash(*syms)
87
+ class_inheritable_reader(*syms)
88
+ class_inheritable_hash_writer(*syms)
89
+ end
90
+
91
+ def inheritable_attributes
92
+ @inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES
93
+ end
94
+
95
+ def write_inheritable_attribute(key, value)
96
+ if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
97
+ @inheritable_attributes = {}
98
+ end
99
+ inheritable_attributes[key] = value
100
+ end
101
+
102
+ def write_inheritable_array(key, elements)
103
+ write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
104
+ write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
105
+ end
106
+
107
+ def write_inheritable_hash(key, hash)
108
+ write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil?
109
+ write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash))
110
+ end
111
+
112
+ def read_inheritable_attribute(key)
113
+ inheritable_attributes[key]
114
+ end
115
+
116
+ def reset_inheritable_attributes
117
+ @inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
118
+ end
119
+
120
+ private
121
+ # Prevent this constant from being created multiple times
122
+ EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES)
123
+
124
+ def inherited_with_inheritable_attributes(child)
125
+ inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
126
+
127
+ if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
128
+ new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
129
+ else
130
+ new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
131
+ memo.update(key => value.duplicable? ? value.dup : value)
132
+ end
133
+ end
134
+
135
+ child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
136
+ end
137
+
138
+ alias inherited_without_inheritable_attributes inherited
139
+ alias inherited inherited_with_inheritable_attributes
140
+ end
@@ -0,0 +1,50 @@
1
+ class Class #:nodoc:
2
+
3
+ # Unassociates the class with its subclasses and removes the subclasses
4
+ # themselves.
5
+ #
6
+ # Integer.remove_subclasses # => [Bignum, Fixnum]
7
+ # Fixnum # => NameError: uninitialized constant Fixnum
8
+ def remove_subclasses
9
+ Object.remove_subclasses_of(self)
10
+ end
11
+
12
+ # Returns an array with the names of the subclasses of +self+ as strings.
13
+ #
14
+ # Integer.subclasses # => ["Bignum", "Fixnum"]
15
+ def subclasses
16
+ Object.subclasses_of(self).map { |o| o.to_s }
17
+ end
18
+
19
+ # Removes the classes in +klasses+ from their parent module.
20
+ #
21
+ # Ordinary classes belong to some module via a constant. This method computes
22
+ # that constant name from the class name and removes it from the module it
23
+ # belongs to.
24
+ #
25
+ # Object.remove_class(Integer) # => [Integer]
26
+ # Integer # => NameError: uninitialized constant Integer
27
+ #
28
+ # Take into account that in general the class object could be still stored
29
+ # somewhere else.
30
+ #
31
+ # i = Integer # => Integer
32
+ # Object.remove_class(Integer) # => [Integer]
33
+ # Integer # => NameError: uninitialized constant Integer
34
+ # i.subclasses # => ["Bignum", "Fixnum"]
35
+ # Fixnum.superclass # => Integer
36
+ def remove_class(*klasses)
37
+ klasses.flatten.each do |klass|
38
+ # Skip this class if there is nothing bound to this name
39
+ next unless defined?(klass.name)
40
+
41
+ basename = klass.to_s.split("::").last
42
+ parent = klass.parent
43
+
44
+ # Skip this class if it does not match the current one bound to this name
45
+ next unless parent.const_defined?(basename) && klass = parent.const_get(basename)
46
+
47
+ parent.instance_eval { remove_const basename } unless parent == klass
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,43 @@
1
+ class Object
2
+ # Can you safely .dup this object?
3
+ # False for nil, false, true, symbols, and numbers; true otherwise.
4
+ def duplicable?
5
+ true
6
+ end
7
+ end
8
+
9
+ class NilClass #:nodoc:
10
+ def duplicable?
11
+ false
12
+ end
13
+ end
14
+
15
+ class FalseClass #:nodoc:
16
+ def duplicable?
17
+ false
18
+ end
19
+ end
20
+
21
+ class TrueClass #:nodoc:
22
+ def duplicable?
23
+ false
24
+ end
25
+ end
26
+
27
+ class Symbol #:nodoc:
28
+ def duplicable?
29
+ false
30
+ end
31
+ end
32
+
33
+ class Numeric #:nodoc:
34
+ def duplicable?
35
+ false
36
+ end
37
+ end
38
+
39
+ class Class #:nodoc:
40
+ def duplicable?
41
+ false
42
+ end
43
+ end
@@ -0,0 +1,72 @@
1
+ module Enumerable
2
+
3
+ # Calculates a sum from the elements. Examples:
4
+ #
5
+ # payments.sum { |p| p.price * p.tax_rate }
6
+ # payments.sum(&:price)
7
+ #
8
+ # The latter is a shortcut for:
9
+ #
10
+ # payments.inject { |sum, p| sum + p.price }
11
+ #
12
+ # It can also calculate the sum without the use of a block.
13
+ #
14
+ # [5, 15, 10].sum # => 30
15
+ # ["foo", "bar"].sum # => "foobar"
16
+ # [[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5]
17
+ #
18
+ # The default sum of an empty list is zero. You can override this default:
19
+ #
20
+ # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
21
+ #
22
+ def sum(identity = 0, &block)
23
+ return identity unless size > 0
24
+
25
+ if block_given?
26
+ map(&block).sum
27
+ else
28
+ inject { |sum, element| sum + element }
29
+ end
30
+ end
31
+
32
+ # Iterates over a collection, passing the current element *and* the
33
+ # +memo+ to the block. Handy for building up hashes or
34
+ # reducing collections down to one object. Examples:
35
+ #
36
+ # %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase } #=> {'foo' => 'FOO', 'bar' => 'BAR'}
37
+ #
38
+ # *Note* that you can't use immutable objects like numbers, true or false as
39
+ # the memo. You would think the following returns 120, but since the memo is
40
+ # never changed, it does not.
41
+ #
42
+ # (1..5).each_with_object(1) { |value, memo| memo *= value } # => 1
43
+ #
44
+ def each_with_object(memo, &block)
45
+ returning memo do |m|
46
+ each do |element|
47
+ block.call(element, m)
48
+ end
49
+ end
50
+ end unless [].respond_to?(:each_with_object)
51
+
52
+ # Convert an enumerable to a hash. Examples:
53
+ #
54
+ # people.index_by(&:login)
55
+ # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
56
+ # people.index_by { |person| "#{person.first_name} #{person.last_name}" }
57
+ # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
58
+ #
59
+ def index_by
60
+ inject({}) do |accum, elem|
61
+ accum[yield(elem)] = elem
62
+ accum
63
+ end
64
+ end
65
+
66
+ # Returns true if the collection has more than 1 element. Functionally equivalent to collection.size > 1.
67
+ # Works with a block too ala any?, so people.many? { |p| p.age > 26 } # => returns true if more than 1 person is over 26.
68
+ def many?(&block)
69
+ size = block_given? ? select(&block).size : self.size
70
+ size > 1
71
+ end
72
+ end
@@ -0,0 +1,6 @@
1
+ require "ant_support/core_ext/hash/keys"
2
+
3
+
4
+ class Hash #:nodoc:
5
+ include AntSupport::CoreExtensions::Hash::Keys
6
+ end
@@ -0,0 +1,52 @@
1
+ module AntSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Hash #:nodoc:
4
+ module Keys
5
+ # Return a new hash with all keys converted to strings.
6
+ def stringify_keys
7
+ inject({}) do |options, (key, value)|
8
+ options[key.to_s] = value
9
+ options
10
+ end
11
+ end
12
+
13
+ # Destructively convert all keys to strings.
14
+ def stringify_keys!
15
+ keys.each do |key|
16
+ self[key.to_s] = delete(key)
17
+ end
18
+ self
19
+ end
20
+
21
+ # Return a new hash with all keys converted to symbols.
22
+ def symbolize_keys
23
+ inject({}) do |options, (key, value)|
24
+ options[(key.to_sym rescue key) || key] = value
25
+ options
26
+ end
27
+ end
28
+
29
+ # Destructively convert all keys to symbols.
30
+ def symbolize_keys!
31
+ self.replace(self.symbolize_keys)
32
+ end
33
+
34
+ alias_method :to_options, :symbolize_keys
35
+ alias_method :to_options!, :symbolize_keys!
36
+
37
+ # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
38
+ # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
39
+ # as keys, this will fail.
40
+ #
41
+ # ==== Examples:
42
+ # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
43
+ # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
44
+ # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
45
+ def assert_valid_keys(*valid_keys)
46
+ unknown_keys = keys - [valid_keys].flatten
47
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,16 @@
1
+ require 'ant_support/core_ext/module/attribute_accessors'
2
+ require 'ant_support/core_ext/module/attr_accessor_with_default'
3
+ require 'ant_support/core_ext/module/aliasing'
4
+
5
+ module AntSupport
6
+ module CoreExtensions
7
+ # Various extensions for the Ruby core Module class.
8
+ module Module
9
+ # Nothing here. Only defined for API documentation purposes.
10
+ end
11
+ end
12
+ end
13
+
14
+ class Module
15
+ include AntSupport::CoreExtensions::Module
16
+ end
@@ -0,0 +1,74 @@
1
+ module AntSupport
2
+ module CoreExtensions
3
+ module Module
4
+ # Encapsulates the common pattern of:
5
+ #
6
+ # alias_method :foo_without_feature, :foo
7
+ # alias_method :foo, :foo_with_feature
8
+ #
9
+ # With this, you simply do:
10
+ #
11
+ # alias_method_chain :foo, :feature
12
+ #
13
+ # And both aliases are set up for you.
14
+ #
15
+ # Query and bang methods (foo?, foo!) keep the same punctuation:
16
+ #
17
+ # alias_method_chain :foo?, :feature
18
+ #
19
+ # is equivalent to
20
+ #
21
+ # alias_method :foo_without_feature?, :foo?
22
+ # alias_method :foo?, :foo_with_feature?
23
+ #
24
+ # so you can safely chain foo, foo?, and foo! with the same feature.
25
+ def alias_method_chain(target, feature)
26
+ # Strip out punctuation on predicates or bang methods since
27
+ # e.g. target?_without_feature is not a valid method name.
28
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
29
+ yield(aliased_target, punctuation) if block_given?
30
+
31
+ with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
32
+
33
+ alias_method without_method, target
34
+ alias_method target, with_method
35
+
36
+ case
37
+ when public_method_defined?(without_method)
38
+ public target
39
+ when protected_method_defined?(without_method)
40
+ protected target
41
+ when private_method_defined?(without_method)
42
+ private target
43
+ end
44
+ end
45
+
46
+ # Allows you to make aliases for attributes, which includes
47
+ # getter, setter, and query methods.
48
+ #
49
+ # Example:
50
+ #
51
+ # class Content < AntMapper::Base
52
+ # # has a title attribute
53
+ # end
54
+ #
55
+ # class Email < Content
56
+ # alias_attribute :subject, :title
57
+ # end
58
+ #
59
+ # e = Email.find(1)
60
+ # e.title # => "Superstars"
61
+ # e.subject # => "Superstars"
62
+ # e.subject? # => true
63
+ # e.subject = "Megastars"
64
+ # e.title # => "Megastars"
65
+ def alias_attribute(new_name, old_name)
66
+ module_eval <<-STR, __FILE__, __LINE__+1
67
+ def #{new_name}; self.#{old_name}; end
68
+ def #{new_name}?; self.#{old_name}?; end
69
+ def #{new_name}=(v); self.#{old_name} = v; end
70
+ STR
71
+ end
72
+ end
73
+ end
74
+ end