ardm-types 1.2.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 (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