dm-types 0.10.2 → 1.0.0.rc1

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 (58) hide show
  1. data/.gitignore +36 -0
  2. data/Gemfile +147 -0
  3. data/Rakefile +7 -8
  4. data/VERSION +1 -1
  5. data/dm-types.gemspec +61 -20
  6. data/lib/dm-types.rb +24 -19
  7. data/lib/dm-types/bcrypt_hash.rb +17 -13
  8. data/lib/dm-types/comma_separated_list.rb +11 -16
  9. data/lib/dm-types/csv.rb +11 -11
  10. data/lib/dm-types/enum.rb +33 -50
  11. data/lib/dm-types/epoch_time.rb +11 -11
  12. data/lib/dm-types/file_path.rb +13 -10
  13. data/lib/dm-types/flag.rb +17 -25
  14. data/lib/dm-types/ip_address.rb +15 -11
  15. data/lib/dm-types/json.rb +17 -14
  16. data/lib/dm-types/paranoid/base.rb +38 -0
  17. data/lib/dm-types/paranoid_boolean.rb +23 -0
  18. data/lib/dm-types/paranoid_datetime.rb +22 -0
  19. data/lib/dm-types/regexp.rb +8 -8
  20. data/lib/dm-types/slug.rb +7 -12
  21. data/lib/dm-types/uri.rb +21 -9
  22. data/lib/dm-types/uuid.rb +18 -11
  23. data/lib/dm-types/yaml.rb +12 -10
  24. data/spec/fixtures/article.rb +0 -2
  25. data/spec/fixtures/bookmark.rb +0 -2
  26. data/spec/fixtures/network_node.rb +0 -2
  27. data/spec/fixtures/person.rb +0 -2
  28. data/spec/fixtures/software_package.rb +0 -2
  29. data/spec/fixtures/ticket.rb +2 -4
  30. data/spec/fixtures/tshirt.rb +3 -5
  31. data/spec/integration/bcrypt_hash_spec.rb +33 -31
  32. data/spec/integration/comma_separated_list_spec.rb +55 -53
  33. data/spec/integration/enum_spec.rb +55 -53
  34. data/spec/integration/file_path_spec.rb +105 -103
  35. data/spec/integration/flag_spec.rb +42 -40
  36. data/spec/integration/ip_address_spec.rb +91 -89
  37. data/spec/integration/json_spec.rb +41 -39
  38. data/spec/integration/slug_spec.rb +36 -34
  39. data/spec/integration/uri_spec.rb +82 -79
  40. data/spec/integration/uuid_spec.rb +63 -61
  41. data/spec/integration/yaml_spec.rb +37 -35
  42. data/spec/spec_helper.rb +7 -36
  43. data/spec/unit/bcrypt_hash_spec.rb +18 -10
  44. data/spec/unit/csv_spec.rb +92 -80
  45. data/spec/unit/enum_spec.rb +27 -42
  46. data/spec/unit/epoch_time_spec.rb +18 -7
  47. data/spec/unit/file_path_spec.rb +15 -10
  48. data/spec/unit/flag_spec.rb +13 -36
  49. data/spec/unit/ip_address_spec.rb +13 -10
  50. data/spec/unit/json_spec.rb +21 -14
  51. data/spec/unit/paranoid_boolean_spec.rb +138 -0
  52. data/spec/unit/paranoid_datetime_spec.rb +143 -0
  53. data/spec/unit/regexp_spec.rb +15 -5
  54. data/spec/unit/uri_spec.rb +13 -9
  55. data/spec/unit/yaml_spec.rb +16 -9
  56. data/tasks/local_gemfile.rake +18 -0
  57. data/tasks/spec.rake +0 -3
  58. metadata +122 -52
@@ -1,3 +1,5 @@
1
+ require 'dm-core'
2
+
1
3
  if RUBY_VERSION >= '1.9.0'
2
4
  require 'csv'
3
5
  else
@@ -6,27 +8,25 @@ else
6
8
  end
7
9
 
8
10
  module DataMapper
9
- module Types
10
- class Csv < DataMapper::Type
11
- primitive Text
12
-
13
- def self.load(value, property)
11
+ class Property
12
+ class Csv < Text
13
+ def load(value)
14
14
  case value
15
- when String then CSV.parse(value)
16
- when Array then value
15
+ when ::String then CSV.parse(value)
16
+ when ::Array then value
17
17
  else
18
18
  nil
19
19
  end
20
20
  end
21
21
 
22
- def self.dump(value, property)
22
+ def dump(value)
23
23
  case value
24
- when Array then CSV.generate { |csv| value.each { |row| csv << row } }
25
- when String then value
24
+ when ::Array then CSV.generate { |csv| value.each { |row| csv << row } }
25
+ when ::String then value
26
26
  else
27
27
  nil
28
28
  end
29
29
  end
30
30
  end # class Csv
31
- end # module Types
31
+ end # class Property
32
32
  end # module DataMapper
@@ -1,73 +1,56 @@
1
- module DataMapper
2
- module Types
3
- class Enum < Type
4
- primitive Integer
1
+ require 'dm-core'
5
2
 
6
- def self.inherited(target)
7
- target.instance_variable_set("@primitive", self.primitive)
8
- end
3
+ module DataMapper
4
+ class Property
5
+ class Enum < Integer
6
+ accept_options :flags
9
7
 
10
- def self.flag_map
11
- @flag_map
12
- end
8
+ attr_reader :flag_map
13
9
 
14
- def self.flag_map=(value)
15
- @flag_map = value
16
- end
10
+ def initialize(model, name, options = {}, type = nil)
11
+ super
17
12
 
18
- def self.new(*flags)
19
- enum = Class.new(Enum)
20
- enum.flag_map = {}
13
+ @flag_map = {}
21
14
 
15
+ flags = options.fetch(:flags)
22
16
  flags.each_with_index do |flag, i|
23
- enum.flag_map[i + 1] = flag
17
+ @flag_map[i + 1] = flag
24
18
  end
25
19
 
26
- enum
20
+ if defined?(::DataMapper::Validations)
21
+ unless model.skip_auto_validation_for?(self)
22
+ if self.class.ancestors.include?(Property::Enum)
23
+ allowed = flag_map.values_at(*flag_map.keys.sort)
24
+ model.validates_within name, model.options_with_message({ :set => allowed }, self, :within)
25
+ end
26
+ end
27
+ end
27
28
  end
28
29
 
29
- def self.[](*flags)
30
- new(*flags)
30
+ def custom?
31
+ true
31
32
  end
32
33
 
33
- def self.load(value, property)
34
- self.flag_map[value]
34
+ def load(value)
35
+ flag_map[value]
35
36
  end
36
37
 
37
- def self.dump(value, property)
38
+ def dump(value)
38
39
  case value
39
- when Array then value.collect { |v| self.dump(v, property) }
40
- else self.flag_map.invert[value]
40
+ when ::Array then value.collect { |v| dump(v) }
41
+ else flag_map.invert[value]
41
42
  end
42
43
  end
43
44
 
44
- def self.typecast(value, property)
45
+ def typecast_to_primitive(value)
45
46
  # Attempt to typecast using the class of the first item in the map.
46
- return value if value.nil?
47
- case self.flag_map[1]
48
- when Symbol then value.to_sym
49
- when String then value.to_s
50
- when Fixnum then value.to_i
51
- else value
52
- end
53
- end
54
-
55
- def self.bind(property)
56
- if defined?(::DataMapper::Validate)
57
- model = property.model
58
-
59
- unless model.skip_auto_validation_for?(property)
60
- if property.type.ancestors.include?(Types::Enum)
61
- model.class_eval do
62
- flag_map = property.type.flag_map
63
- allowed = flag_map.values_at(*flag_map.keys.sort)
64
-
65
- validates_within property.name, options_with_message({ :set => allowed }, property, :within)
66
- end
67
- end
68
- end
47
+ case flag_map[1]
48
+ when ::Symbol then value.to_sym
49
+ when ::String then value.to_s
50
+ when ::Fixnum then value.to_i
51
+ else value
69
52
  end
70
53
  end
71
54
  end # class Enum
72
- end # module Types
55
+ end # class Property
73
56
  end # module DataMapper
@@ -1,22 +1,22 @@
1
- module DataMapper
2
- module Types
3
- class EpochTime < DataMapper::Type
4
- primitive Integer
1
+ require 'dm-core'
5
2
 
6
- def self.load(value, property)
7
- if value.kind_of?(Integer)
8
- Time.at(value)
3
+ module DataMapper
4
+ class Property
5
+ class EpochTime < Integer
6
+ def load(value)
7
+ if value.kind_of?(::Integer)
8
+ ::Time.at(value)
9
9
  else
10
10
  value
11
11
  end
12
12
  end
13
13
 
14
- def self.dump(value, property)
14
+ def dump(value)
15
15
  case value
16
- when Integer, Time then value.to_i
17
- when DateTime then value.to_time.to_i
16
+ when ::Integer, ::Time then value.to_i
17
+ when ::DateTime then value.to_time.to_i
18
18
  end
19
19
  end
20
20
  end # class EpochTime
21
- end # module Types
21
+ end # class Property
22
22
  end # module DataMapper
@@ -1,12 +1,16 @@
1
1
  require 'pathname'
2
+ require 'dm-core'
2
3
 
3
4
  module DataMapper
4
- module Types
5
- class FilePath < DataMapper::Type
6
- primitive String
7
- length 255
5
+ class Property
6
+ class FilePath < String
7
+ length 255
8
8
 
9
- def self.load(value, property)
9
+ def primitive?(value)
10
+ value.kind_of?(Pathname)
11
+ end
12
+
13
+ def load(value)
10
14
  if value.blank?
11
15
  nil
12
16
  else
@@ -14,15 +18,14 @@ module DataMapper
14
18
  end
15
19
  end
16
20
 
17
- def self.dump(value, property)
21
+ def dump(value)
18
22
  return nil if value.blank?
19
23
  value.to_s
20
24
  end
21
25
 
22
- def self.typecast(value, property)
23
- # Leave alone if a Pathname is given.
24
- value.kind_of?(Pathname) ? value : load(value, property)
26
+ def typecast_to_primitive(value)
27
+ load(value)
25
28
  end
26
29
  end # class FilePath
27
- end # module Types
30
+ end # class Property
28
31
  end # module DataMapper
@@ -1,36 +1,28 @@
1
- module DataMapper
2
- module Types
3
- class Flag < Type
4
- primitive Integer
1
+ require 'dm-core'
5
2
 
6
- def self.inherited(target)
7
- target.instance_variable_set('@primitive', self.primitive)
8
- end
3
+ module DataMapper
4
+ class Property
5
+ class Flag < Integer
6
+ accept_options :flags
9
7
 
10
- def self.flag_map
11
- @flag_map
12
- end
8
+ attr_reader :flag_map
13
9
 
14
- def self.flag_map=(value)
15
- @flag_map = value
16
- end
10
+ def initialize(model, name, options = {}, type = nil)
11
+ super
17
12
 
18
- def self.new(*flags)
19
- type = Class.new(Flag)
20
- type.flag_map = {}
13
+ @flag_map = {}
21
14
 
15
+ flags = options.fetch(:flags)
22
16
  flags.each_with_index do |flag, i|
23
- type.flag_map[i] = flag
17
+ flag_map[i] = flag
24
18
  end
25
-
26
- type
27
19
  end
28
20
 
29
- def self.[](*flags)
30
- new(*flags)
21
+ def custom?
22
+ true
31
23
  end
32
24
 
33
- def self.load(value, property)
25
+ def load(value)
34
26
  return [] if value.nil? || value <= 0
35
27
 
36
28
  begin
@@ -46,13 +38,13 @@ module DataMapper
46
38
  end
47
39
  end
48
40
 
49
- def self.dump(value, property)
41
+ def dump(value)
50
42
  return if value.nil?
51
43
  flags = Array(value).map { |flag| flag.to_sym }.flatten
52
44
  flag_map.invert.values_at(*flags).compact.inject(0) { |sum, i| sum += 1 << i }
53
45
  end
54
46
 
55
- def self.typecast(value, property)
47
+ def typecast(value)
56
48
  case value
57
49
  when nil then nil
58
50
  when Array then value.map {|v| v.to_sym}
@@ -60,5 +52,5 @@ module DataMapper
60
52
  end
61
53
  end
62
54
  end # class Flag
63
- end # module Types
55
+ end # class Property
64
56
  end # module DataMapper
@@ -1,31 +1,35 @@
1
1
  require 'ipaddr'
2
+ require 'dm-core'
2
3
 
3
4
  module DataMapper
4
- module Types
5
- class IPAddress < DataMapper::Type
6
- primitive String
7
- length 16
5
+ class Property
6
+ class IPAddress < String
7
+ length 39
8
8
 
9
- def self.load(value, property)
9
+ def primitive?(value)
10
+ value.kind_of?(IPAddr)
11
+ end
12
+
13
+ def load(value)
10
14
  if value.nil?
11
15
  nil
12
- elsif value.is_a?(String) && !value.empty?
16
+ elsif value.is_a?(::String) && !value.empty?
13
17
  IPAddr.new(value)
14
- elsif value.is_a?(String) && value.empty?
18
+ elsif value.is_a?(::String) && value.empty?
15
19
  IPAddr.new("0.0.0.0")
16
20
  else
17
21
  raise ArgumentError.new("+value+ must be nil or a String")
18
22
  end
19
23
  end
20
24
 
21
- def self.dump(value, property)
25
+ def dump(value)
22
26
  return nil if value.nil?
23
27
  value.to_s
24
28
  end
25
29
 
26
- def self.typecast(value, property)
27
- value.kind_of?(IPAddr) ? value : load(value, property)
30
+ def typecast_to_primitive(value)
31
+ load(value)
28
32
  end
29
33
  end # class IPAddress
30
- end # module Types
34
+ end # module Property
31
35
  end # module DataMapper
@@ -1,36 +1,39 @@
1
+ require 'dm-core'
1
2
  require 'json'
2
3
 
3
4
  module DataMapper
4
- module Types
5
- class Json < DataMapper::Type
6
- primitive Text
5
+ class Property
6
+ class Json < Text
7
+ def custom?
8
+ true
9
+ end
10
+
11
+ def primitive?(value)
12
+ value.kind_of?(::Array) || value.kind_of?(::Hash)
13
+ end
7
14
 
8
- def self.load(value, property)
15
+ def load(value)
9
16
  if value.nil?
10
17
  nil
11
- elsif value.is_a?(String)
18
+ elsif value.is_a?(::String)
12
19
  ::JSON.load(value)
13
20
  else
14
21
  raise ArgumentError.new("+value+ of a property of JSON type must be nil or a String")
15
22
  end
16
23
  end
17
24
 
18
- def self.dump(value, property)
19
- if value.nil? || value.is_a?(String)
25
+ def dump(value)
26
+ if value.nil? || value.is_a?(::String)
20
27
  value
21
28
  else
22
29
  ::JSON.dump(value)
23
30
  end
24
31
  end
25
32
 
26
- def self.typecast(value, property)
27
- if value.nil? || value.kind_of?(Array) || value.kind_of?(Hash)
28
- value
29
- else
30
- ::JSON.load(value.to_s)
31
- end
33
+ def typecast_to_primitive(value)
34
+ ::JSON.load(value.to_s)
32
35
  end
33
36
  end # class Json
34
37
  JSON = Json
35
- end # module Types
38
+ end # class Property
36
39
  end # module DataMapper
@@ -0,0 +1,38 @@
1
+ module DataMapper
2
+ module Types
3
+ module Paranoid
4
+ module Base
5
+ def self.included(model)
6
+ model.extend ClassMethods
7
+ end
8
+
9
+ def paranoid_destroy
10
+ model.paranoid_properties.each do |name, block|
11
+ attribute_set(name, block.call(self))
12
+ end
13
+ save_self
14
+ self.persisted_state = Resource::State::Immutable.new(self)
15
+ true
16
+ end
17
+
18
+ private
19
+
20
+ # @api private
21
+ def _destroy(execute_hooks = true)
22
+ return false unless saved?
23
+ if execute_hooks
24
+ paranoid_destroy
25
+ else
26
+ super
27
+ end
28
+ end
29
+ end # module Base
30
+
31
+ module ClassMethods
32
+ def with_deleted
33
+ with_exclusive_scope({}) { block_given? ? yield : all }
34
+ end
35
+ end # module ClassMethods
36
+ end # module Paranoid
37
+ end # module Types
38
+ end # module DataMapper
@@ -0,0 +1,23 @@
1
+ require 'dm-types/paranoid/base'
2
+
3
+ module DataMapper
4
+ class Property
5
+ class ParanoidBoolean < Boolean
6
+ default false
7
+ lazy true
8
+
9
+ # @api private
10
+ def bind
11
+ property_name = name.inspect
12
+
13
+ model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
14
+ include Paranoid::Base
15
+
16
+ set_paranoid_property(#{property_name}) { true }
17
+
18
+ default_scope(#{repository_name.inspect}).update(#{property_name} => false)
19
+ RUBY
20
+ end
21
+ end # class ParanoidBoolean
22
+ end # module Property
23
+ end # module DataMapper