ardm-types 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +36 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +51 -0
  5. data/LICENSE +20 -0
  6. data/README.rdoc +3 -0
  7. data/Rakefile +4 -0
  8. data/ardm-types.gemspec +29 -0
  9. data/lib/ardm-types.rb +1 -0
  10. data/lib/dm-types/api_key.rb +30 -0
  11. data/lib/dm-types/bcrypt_hash.rb +34 -0
  12. data/lib/dm-types/comma_separated_list.rb +29 -0
  13. data/lib/dm-types/csv.rb +38 -0
  14. data/lib/dm-types/enum.rb +51 -0
  15. data/lib/dm-types/epoch_time.rb +41 -0
  16. data/lib/dm-types/file_path.rb +32 -0
  17. data/lib/dm-types/flag.rb +63 -0
  18. data/lib/dm-types/ip_address.rb +42 -0
  19. data/lib/dm-types/json.rb +50 -0
  20. data/lib/dm-types/paranoid/base.rb +55 -0
  21. data/lib/dm-types/paranoid_boolean.rb +23 -0
  22. data/lib/dm-types/paranoid_datetime.rb +22 -0
  23. data/lib/dm-types/regexp.rb +21 -0
  24. data/lib/dm-types/slug.rb +29 -0
  25. data/lib/dm-types/support/dirty_minder.rb +166 -0
  26. data/lib/dm-types/support/flags.rb +41 -0
  27. data/lib/dm-types/uri.rb +38 -0
  28. data/lib/dm-types/uuid.rb +74 -0
  29. data/lib/dm-types/version.rb +5 -0
  30. data/lib/dm-types/yaml.rb +41 -0
  31. data/lib/dm-types.rb +23 -0
  32. data/spec/fixtures/api_user.rb +14 -0
  33. data/spec/fixtures/article.rb +35 -0
  34. data/spec/fixtures/bookmark.rb +23 -0
  35. data/spec/fixtures/invention.rb +7 -0
  36. data/spec/fixtures/network_node.rb +36 -0
  37. data/spec/fixtures/person.rb +25 -0
  38. data/spec/fixtures/software_package.rb +33 -0
  39. data/spec/fixtures/ticket.rb +21 -0
  40. data/spec/fixtures/tshirt.rb +24 -0
  41. data/spec/integration/api_key_spec.rb +27 -0
  42. data/spec/integration/bcrypt_hash_spec.rb +47 -0
  43. data/spec/integration/comma_separated_list_spec.rb +87 -0
  44. data/spec/integration/dirty_minder_spec.rb +197 -0
  45. data/spec/integration/enum_spec.rb +80 -0
  46. data/spec/integration/epoch_time_spec.rb +61 -0
  47. data/spec/integration/file_path_spec.rb +160 -0
  48. data/spec/integration/flag_spec.rb +72 -0
  49. data/spec/integration/ip_address_spec.rb +153 -0
  50. data/spec/integration/json_spec.rb +72 -0
  51. data/spec/integration/slug_spec.rb +67 -0
  52. data/spec/integration/uri_spec.rb +139 -0
  53. data/spec/integration/uuid_spec.rb +102 -0
  54. data/spec/integration/yaml_spec.rb +69 -0
  55. data/spec/rcov.opts +6 -0
  56. data/spec/shared/flags_shared_spec.rb +37 -0
  57. data/spec/shared/identity_function_group.rb +5 -0
  58. data/spec/spec.opts +4 -0
  59. data/spec/spec_helper.rb +30 -0
  60. data/spec/unit/bcrypt_hash_spec.rb +155 -0
  61. data/spec/unit/csv_spec.rb +142 -0
  62. data/spec/unit/enum_spec.rb +126 -0
  63. data/spec/unit/epoch_time_spec.rb +74 -0
  64. data/spec/unit/file_path_spec.rb +87 -0
  65. data/spec/unit/flag_spec.rb +114 -0
  66. data/spec/unit/ip_address_spec.rb +121 -0
  67. data/spec/unit/json_spec.rb +144 -0
  68. data/spec/unit/paranoid_boolean_spec.rb +150 -0
  69. data/spec/unit/paranoid_datetime_spec.rb +154 -0
  70. data/spec/unit/regexp_spec.rb +63 -0
  71. data/spec/unit/uri_spec.rb +64 -0
  72. data/spec/unit/uuid_spec.rb +25 -0
  73. data/spec/unit/yaml_spec.rb +111 -0
  74. data/tasks/spec.rake +38 -0
  75. data/tasks/yard.rake +9 -0
  76. data/tasks/yardstick.rake +19 -0
  77. metadata +236 -0
@@ -0,0 +1,21 @@
1
+ require 'dm-core'
2
+
3
+ module DataMapper
4
+ class Property
5
+ class Regexp < String
6
+
7
+ def load(value)
8
+ ::Regexp.new(value) unless value.nil?
9
+ end
10
+
11
+ def dump(value)
12
+ value.source unless value.nil?
13
+ end
14
+
15
+ def typecast(value)
16
+ load(value)
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ require 'dm-core'
2
+ require 'stringex'
3
+
4
+ module DataMapper
5
+ class Property
6
+ class Slug < String
7
+
8
+ # Maximum length chosen because URI type is limited to 2000
9
+ # characters, and a slug is a component of a URI, so it should
10
+ # not exceed the maximum URI length either.
11
+ length 2000
12
+
13
+ def typecast(value)
14
+ if value.nil?
15
+ nil
16
+ elsif value.respond_to?(:to_str)
17
+ escape(value.to_str)
18
+ else
19
+ raise ArgumentError, '+value+ must be nil or respond to #to_str'
20
+ end
21
+ end
22
+
23
+ def escape(string)
24
+ string.to_url
25
+ end
26
+
27
+ end # class Slug
28
+ end # class Property
29
+ end # module DataMapper
@@ -0,0 +1,166 @@
1
+ # Approach
2
+ #
3
+ # We need to detect whether or not the underlying Hash or Array changed and
4
+ # update the dirty-ness of the encapsulating Resource accordingly (so that it
5
+ # will actually save).
6
+ #
7
+ # DM's state-tracking code only triggers dirty-ness by comparing the new value
8
+ # against the instance's Property's current value. WRT mutation, we have to
9
+ # choose one of the following approaches:
10
+ #
11
+ # (1) mutate a copy ("after"), then invoke the Resource assignment and State
12
+ # tracking
13
+ #
14
+ # (2) create a copy ("before"), mutate self ("after"), then invoke the
15
+ # Resource assignment and State tracking
16
+ #
17
+ # (1) seemed simpler at first, but it required additional steps to alias the
18
+ # original (pre-hooked) methods before overriding them (so they could be invoked
19
+ # externally, ala self.clone.send("orig_...")), and more importantly it resulted
20
+ # in any external references keeping their old value (instead of getting the
21
+ # new), like so:
22
+ #
23
+ # copy = instance.json
24
+ # copy[:some] = :value
25
+ # instance.json[:some] == :value
26
+ # => true
27
+ # copy[:some] == :value
28
+ # => false # fk!
29
+ #
30
+ # In order to do (2) and still have State tracking trigger normally, we need to
31
+ # ensure the Property has a different value other than self when the State
32
+ # tracking does the comparison. This equates to setting the Property directly
33
+ # to the "before" value (a clone and thus a different object/value) before
34
+ # invoking the Resource Property/attribute assignment.
35
+ #
36
+ # The cloning of any value might sound expensive, but it's identical in cost to
37
+ # what you already had to do: assign a cloned copy in order to trigger
38
+ # dirty-ness (e.g. ::DataMapper::Property::Json):
39
+ #
40
+ # model.json = model.json.merge({:some=>:value})
41
+ #
42
+ # Hooking Core Classes
43
+ #
44
+ # We want to hook certain methods on Hash and Array to trigger dirty-ness in the
45
+ # resource. However, because these are core classes, they are individually
46
+ # mapped to C primitives and thus cannot be hooked through #send/#__send__. We
47
+ # have to override each method, but we don't want to write a lot of code.
48
+ #
49
+ # Minimally Invasive
50
+ #
51
+ # We also want to extend behaviour of existing class instances instead of
52
+ # impersonating/delegating from a proxy class of our own, or overriding a global
53
+ # class behaviour. This is the most flexible approach and least prone to error,
54
+ # since it leaves open the option for consumers to proxy or override global
55
+ # classes, and is less likely to interfere with method_missing/etc shenanigans.
56
+ #
57
+ # Nested Object Mutations
58
+ #
59
+ # Since we use {Array,Hash}#hash to compare before & after, and #hash accounts
60
+ # for/traverses nested structures, no "deep" inspection logic is technically
61
+ # necessary. However, Resource#dirty? only queries a cache of dirtied
62
+ # attributes, whose own population strategy is to hook assignment (instead of
63
+ # interrogating properties on demand). So the approach is still limited to
64
+ # top-level mutators.
65
+ #
66
+ # Maybe consider optional "advisory" Property#dirty? method for Resource#dirty?
67
+ # that custom properties could use for this purpose.
68
+ #
69
+ # TODO: add support for detecting mutations in nested objects, but we can't
70
+ # catch the assignment from here (yet?).
71
+ # TODO: ensure we covered all indirectly-mutable classes that DM uses underneath
72
+ # a property type
73
+ # TODO: figure out how to hook core class methods on RBX (which do use #send)
74
+
75
+ module DataMapper
76
+ class Property
77
+ module DirtyMinder
78
+
79
+ module Hooker
80
+ MUTATION_METHODS = {
81
+ ::Array => %w{
82
+ []= push << shift pop insert unshift delete
83
+ delete_at replace fill clear
84
+ slice! reverse! rotate! compact! flatten! uniq!
85
+ collect! map! sort! sort_by! reject! delete_if!
86
+ select! shuffle!
87
+ }.select { |meth| ::Array.instance_methods.any? { |m| m.to_s == meth } },
88
+
89
+ ::Hash => %w{
90
+ []= store delete delete_if replace update
91
+ delete rehash shift clear
92
+ merge! reject! select!
93
+ }.select { |meth| ::Hash.instance_methods.any? { |m| m.to_s == meth } },
94
+ }
95
+
96
+ def self.extended(instance)
97
+ # FIXME: DirtyMinder is currently unsupported on RBX, because unlike
98
+ # the other supported Rubies, RBX core class (e.g. Array, Hash)
99
+ # methods use #send(). In other words, the other Rubies don't use
100
+ # #send() (they map directly to their C functions).
101
+ #
102
+ # The current methodology takes advantage of this by using #send() to
103
+ # forward method invocations we've hooked. Supporting RBX will
104
+ # require finding another way, possibly for all Rubies. In the
105
+ # meantime, something is better than nothing.
106
+ return if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'rbx'
107
+
108
+ return unless type = MUTATION_METHODS.keys.find { |k| instance.kind_of?(k) }
109
+ instance.extend const_get("#{type}Hooks")
110
+ end
111
+
112
+ MUTATION_METHODS.each do |klass, methods|
113
+ methods.each do |meth|
114
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
115
+ module #{klass}Hooks
116
+ def #{meth}(*)
117
+ before = self.clone
118
+ ret = super
119
+ after = self
120
+
121
+ # If the hashes aren't equivalent then we know the Resource
122
+ # should be dirty. However because we mutated self, normal
123
+ # State tracking will never trigger, because it will compare the
124
+ # new value - self - to the Resource's existing property value -
125
+ # which is also self.
126
+ #
127
+ # The solution is to drop 1 level beneath Resource State
128
+ # tracking and set the value of the property directly to the
129
+ # previous value (a different object now, because it's a clone).
130
+ # Then trigger the State tracking like normal.
131
+ if before.hash != after.hash
132
+ @property.set(@resource, before)
133
+ @resource.attribute_set(@property.name, after)
134
+ end
135
+
136
+ ret
137
+ end
138
+ end
139
+ RUBY
140
+ end
141
+ end
142
+
143
+ def track(resource, property)
144
+ @resource, @property = resource, property
145
+ end
146
+
147
+ end # Hooker
148
+
149
+ # Catch any direct assignment (#set), and any Resource#reload (set!).
150
+ def set!(resource, value)
151
+ hook_value(resource, value) unless value.kind_of? Hooker
152
+ super
153
+ end
154
+
155
+ private
156
+
157
+ def hook_value(resource, value)
158
+ return if value.kind_of? Hooker
159
+
160
+ value.extend Hooker
161
+ value.track(resource, self)
162
+ end
163
+
164
+ end # DirtyMinder
165
+ end # Property
166
+ end # DataMapper
@@ -0,0 +1,41 @@
1
+ module DataMapper
2
+ class Property
3
+ module Flags
4
+ def self.included(base)
5
+ base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
6
+ extend DataMapper::Property::Flags::ClassMethods
7
+
8
+ accept_options :flags
9
+ attr_reader :flag_map
10
+
11
+ class << self
12
+ attr_accessor :generated_classes
13
+ end
14
+
15
+ self.generated_classes = {}
16
+ RUBY
17
+ end
18
+
19
+ def custom?
20
+ true
21
+ end
22
+
23
+ module ClassMethods
24
+ # TODO: document
25
+ # @api public
26
+ def [](*values)
27
+ if klass = generated_classes[values.flatten]
28
+ klass
29
+ else
30
+ klass = ::Class.new(self)
31
+ klass.flags(values)
32
+
33
+ generated_classes[values.flatten] = klass
34
+
35
+ klass
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ require 'addressable/uri'
2
+ require 'dm-core'
3
+
4
+ module DataMapper
5
+ class Property
6
+ class URI < String
7
+
8
+ # Maximum length chosen based on recommendation:
9
+ # http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-an-url
10
+ length 2000
11
+
12
+ def custom?
13
+ true
14
+ end
15
+
16
+ def primitive?(value)
17
+ value.kind_of?(Addressable::URI)
18
+ end
19
+
20
+ def valid?(value, negated = false)
21
+ super || primitive?(value) || value.kind_of?(::String)
22
+ end
23
+
24
+ def load(value)
25
+ Addressable::URI.parse(value)
26
+ end
27
+
28
+ def dump(value)
29
+ value.to_s unless value.nil?
30
+ end
31
+
32
+ def typecast_to_primitive(value)
33
+ load(value)
34
+ end
35
+
36
+ end # class URI
37
+ end # class Property
38
+ end # module DataMapper
@@ -0,0 +1,74 @@
1
+ require 'dm-core'
2
+ require 'uuidtools' # must be ~>2.0
3
+
4
+ module DataMapper
5
+ class Property
6
+ # UUID Type
7
+ # First run at this, because I need it. A few caveats:
8
+ # * Only works on postgres, using the built-in native uuid type.
9
+ # To make it work in mysql, you'll have to add a typemap entry to
10
+ # the mysql_adapter. I think. I don't have mysql handy, so I'm
11
+ # not going to try. For SQLite, this will have to inherit from the
12
+ # String primitive
13
+ # * Won't accept a random default, because of the namespace clash
14
+ # between this and the UUIDtools gem. Also can't set the default
15
+ # type to UUID() (postgres-contrib's native generator) and
16
+ # automigrate, because auto_migrate! tries to make it a string "UUID()"
17
+ # Feel free to enchance this, and delete these caveats when they're fixed.
18
+ #
19
+ # -- Rando Sept 25, 08
20
+ #
21
+ # Actually, setting the primitive to "UUID" is not neccessary and causes
22
+ # a segfault when trying to query uuid's from the database. The primitive
23
+ # should be a class which has been added to the do driver you are using.
24
+ # Also, it's only neccessary to add a class to the do drivers to use as a
25
+ # primitive when a value cannot be represented as a string. A uuid can be
26
+ # represented as a string, so setting the primitive to String ensures that
27
+ # the value argument is a String containing the uuid in string form.
28
+ #
29
+ # <strike>It is still neccessary to add the UUID entry to the type map for
30
+ # each different adapter with their respective database primitive.</strike>
31
+ #
32
+ # The method that generates the SQL schema from the typemap currently
33
+ # ignores the size attribute from the type map if the primitive type
34
+ # is String. The causes the generated SQL statement to contain a size for
35
+ # a UUID column (e.g. id UUID(50)), which causes a syntax error in postgres.
36
+ # Until this is resolved, you will have to manually change the column type
37
+ # to UUID in a migration, if you want to use postgres' built in UUID type.
38
+ #
39
+ # -- benburkert Nov 15, 08
40
+ #
41
+ class UUID < String
42
+
43
+ length 36
44
+
45
+ # We need to override this method otherwise typecast_to_primitive won't be called.
46
+ # In the future we will set primitive to UUIDTools::UUID but this can happen only
47
+ # when adapters can handle it
48
+ def primitive?(value)
49
+ value.kind_of?(UUIDTools::UUID)
50
+ end
51
+
52
+ def valid?(value, negated = false)
53
+ super || dump(value).kind_of?(::String)
54
+ end
55
+
56
+ def dump(value)
57
+ value.to_s unless value.nil?
58
+ end
59
+
60
+ def load(value)
61
+ if primitive?(value)
62
+ value
63
+ elsif !value.nil?
64
+ UUIDTools::UUID.parse(value)
65
+ end
66
+ end
67
+
68
+ def typecast_to_primitive(value)
69
+ load(value)
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,5 @@
1
+ module DataMapper
2
+ module Types
3
+ VERSION = '1.2.2'
4
+ end
5
+ end
@@ -0,0 +1,41 @@
1
+ require 'yaml'
2
+ require 'dm-core'
3
+ require 'dm-types/support/dirty_minder'
4
+
5
+ module DataMapper
6
+ class Property
7
+ class Yaml < Text
8
+
9
+ def custom?
10
+ true
11
+ end
12
+
13
+ def load(value)
14
+ if value.nil?
15
+ nil
16
+ elsif value.is_a?(::String)
17
+ ::YAML.load(value)
18
+ else
19
+ raise ArgumentError, '+value+ of a property of YAML type must be nil or a String'
20
+ end
21
+ end
22
+
23
+ def dump(value)
24
+ if value.nil?
25
+ nil
26
+ elsif value.is_a?(::String) && value =~ /^---/
27
+ value
28
+ else
29
+ ::YAML.dump(value)
30
+ end
31
+ end
32
+
33
+ def typecast(value)
34
+ value
35
+ end
36
+
37
+ include ::DataMapper::Property::DirtyMinder
38
+
39
+ end # class Yaml
40
+ end # class Property
41
+ end # module DataMapper
data/lib/dm-types.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'dm-core'
2
+
3
+ module DataMapper
4
+ class Property
5
+ autoload :CommaSeparatedList, 'dm-types/comma_separated_list'
6
+ autoload :Csv, 'dm-types/csv'
7
+ autoload :BCryptHash, 'dm-types/bcrypt_hash'
8
+ autoload :Enum, 'dm-types/enum'
9
+ autoload :EpochTime, 'dm-types/epoch_time'
10
+ autoload :FilePath, 'dm-types/file_path'
11
+ autoload :Flag, 'dm-types/flag'
12
+ autoload :IPAddress, 'dm-types/ip_address'
13
+ autoload :Json, 'dm-types/json'
14
+ autoload :Regexp, 'dm-types/regexp'
15
+ autoload :ParanoidBoolean, 'dm-types/paranoid_boolean'
16
+ autoload :ParanoidDateTime, 'dm-types/paranoid_datetime'
17
+ autoload :Slug, 'dm-types/slug'
18
+ autoload :UUID, 'dm-types/uuid'
19
+ autoload :URI, 'dm-types/uri'
20
+ autoload :Yaml, 'dm-types/yaml'
21
+ autoload :APIKey, 'dm-types/api_key'
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ module DataMapper
2
+ module TypesFixtures
3
+ class APIUser
4
+
5
+ include DataMapper::Resource
6
+
7
+ property :id, Serial
8
+
9
+ property :name, String
10
+
11
+ property :api_key, APIKey
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,35 @@
1
+ module DataMapper
2
+ module TypesFixtures
3
+
4
+ class Article
5
+ #
6
+ # Behaviors
7
+ #
8
+
9
+ include ::DataMapper::Resource
10
+
11
+ #
12
+ # Properties
13
+ #
14
+
15
+ property :id, Serial
16
+
17
+ property :title, String, :length => 255
18
+ property :body, Text
19
+
20
+ property :created_at, DateTime
21
+ property :updated_at, DateTime
22
+ property :published_at, DateTime
23
+
24
+ property :slug, Slug
25
+
26
+ #
27
+ # Hooks
28
+ #
29
+
30
+ before :valid? do
31
+ self.slug = self.title
32
+ end
33
+ end # Article
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ module DataMapper
2
+ module TypesFixtures
3
+
4
+ class Bookmark
5
+ #
6
+ # Behaviors
7
+ #
8
+
9
+ include ::DataMapper::Resource
10
+
11
+ #
12
+ # Properties
13
+ #
14
+
15
+ property :id, Serial
16
+
17
+ property :title, String, :length => 255
18
+ property :shared, Boolean
19
+ property :uri, URI
20
+ property :tags, Yaml
21
+ end # Bookmark
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ module DataMapper
2
+ module TypesFixtures
3
+
4
+ Invention = Struct.new(:name)
5
+
6
+ end
7
+ end
@@ -0,0 +1,36 @@
1
+ module DataMapper
2
+ module TypesFixtures
3
+
4
+ class NetworkNode
5
+ #
6
+ # Behaviors
7
+ #
8
+
9
+ include ::DataMapper::Resource
10
+
11
+ #
12
+ # Properties
13
+ #
14
+
15
+ property :id, Serial
16
+ property :ip_address, IPAddress
17
+ property :cidr_subnet_bits, Integer
18
+ property :node_uuid, UUID
19
+
20
+ #
21
+ # API
22
+ #
23
+
24
+ alias_method :uuid, :node_uuid
25
+ alias_method :uuid=, :node_uuid=
26
+
27
+ def runs_ipv6?
28
+ self.ip_address.ipv6?
29
+ end
30
+
31
+ def runs_ipv4?
32
+ self.ip_address.ipv4?
33
+ end
34
+ end # NetworkNode
35
+ end # TypesFixtures
36
+ end # DataMapper
@@ -0,0 +1,25 @@
1
+ module DataMapper
2
+ module TypesFixtures
3
+ class Person
4
+ #
5
+ # Behaviors
6
+ #
7
+
8
+ include DataMapper::Resource
9
+
10
+ #
11
+ # Properties
12
+ #
13
+
14
+ property :id, Serial
15
+ property :name, String
16
+ property :positions, Json
17
+ property :inventions, Yaml
18
+ property :birthday, EpochTime
19
+
20
+ property :interests, CommaSeparatedList
21
+
22
+ property :password, BCryptHash
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ module DataMapper
2
+ module TypesFixtures
3
+
4
+ class SoftwarePackage
5
+ #
6
+ # Behaviors
7
+ #
8
+
9
+ include ::DataMapper::Resource
10
+
11
+ #
12
+ # Properties
13
+ #
14
+
15
+ property :id, Serial
16
+ without_auto_validations do
17
+ property :node_number, Integer, :index => true
18
+
19
+ property :source_path, FilePath
20
+ property :destination_path, FilePath
21
+
22
+ property :product, String
23
+ property :version, String
24
+ property :released_at, DateTime
25
+
26
+ property :security_update, Boolean
27
+
28
+ property :installed_at, DateTime
29
+ property :installed_by, String
30
+ end
31
+ end # SoftwarePackage
32
+ end # TypesFixtures
33
+ end # DataMapper
@@ -0,0 +1,21 @@
1
+ module DataMapper
2
+ module TypesFixtures
3
+ class Ticket
4
+ #
5
+ # Behaviors
6
+ #
7
+
8
+ include DataMapper::Resource
9
+ include DataMapper::Validations
10
+
11
+ #
12
+ # Properties
13
+ #
14
+
15
+ property :id, Serial
16
+ property :title, String, :length => 255
17
+ property :body, Text
18
+ property :status, Enum[:unconfirmed, :confirmed, :assigned, :resolved, :not_applicable]
19
+ end # Ticket
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ module DataMapper
2
+ module TypesFixtures
3
+
4
+ class TShirt
5
+ #
6
+ # Behaviors
7
+ #
8
+
9
+ include ::DataMapper::Resource
10
+
11
+ #
12
+ # Properties
13
+ #
14
+
15
+ property :id, Serial
16
+ property :writing, String
17
+ property :has_picture, Boolean, :default => false
18
+ property :picture, Enum[:octocat, :fork_you, :git_down]
19
+
20
+ property :color, Enum[:white, :black, :red, :orange, :yellow, :green, :cyan, :blue, :purple]
21
+ property :size, Flag[:xs, :small, :medium, :large, :xl, :xxl], :default => :xs
22
+ end # Shirt
23
+ end # TypesFixtures
24
+ end # DataMapper