ardm 0.0.1

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 (179) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +35 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE +21 -0
  5. data/README.md +70 -0
  6. data/Rakefile +4 -0
  7. data/ardm.gemspec +29 -0
  8. data/db/.gitignore +1 -0
  9. data/lib/ardm/active_record/associations.rb +80 -0
  10. data/lib/ardm/active_record/base.rb +49 -0
  11. data/lib/ardm/active_record/dirty.rb +25 -0
  12. data/lib/ardm/active_record/hooks.rb +31 -0
  13. data/lib/ardm/active_record/inheritance.rb +37 -0
  14. data/lib/ardm/active_record/is/state_machine.rb +21 -0
  15. data/lib/ardm/active_record/is.rb +22 -0
  16. data/lib/ardm/active_record/not_found.rb +7 -0
  17. data/lib/ardm/active_record/predicate_builder/array_handler.rb +31 -0
  18. data/lib/ardm/active_record/predicate_builder/rails3.rb +147 -0
  19. data/lib/ardm/active_record/predicate_builder/rails4.rb +139 -0
  20. data/lib/ardm/active_record/predicate_builder/relation_handler.rb +15 -0
  21. data/lib/ardm/active_record/predicate_builder.rb +19 -0
  22. data/lib/ardm/active_record/property.rb +357 -0
  23. data/lib/ardm/active_record/query.rb +108 -0
  24. data/lib/ardm/active_record/record.rb +70 -0
  25. data/lib/ardm/active_record/relation.rb +83 -0
  26. data/lib/ardm/active_record/repository.rb +38 -0
  27. data/lib/ardm/active_record/serialization.rb +164 -0
  28. data/lib/ardm/active_record/storage_names.rb +28 -0
  29. data/lib/ardm/active_record/validations.rb +111 -0
  30. data/lib/ardm/active_record.rb +43 -0
  31. data/lib/ardm/data_mapper/not_found.rb +5 -0
  32. data/lib/ardm/data_mapper/record.rb +41 -0
  33. data/lib/ardm/data_mapper.rb +5 -0
  34. data/lib/ardm/env.rb +5 -0
  35. data/lib/ardm/property/api_key.rb +30 -0
  36. data/lib/ardm/property/bcrypt_hash.rb +31 -0
  37. data/lib/ardm/property/binary.rb +23 -0
  38. data/lib/ardm/property/boolean.rb +29 -0
  39. data/lib/ardm/property/class.rb +19 -0
  40. data/lib/ardm/property/comma_separated_list.rb +28 -0
  41. data/lib/ardm/property/csv.rb +35 -0
  42. data/lib/ardm/property/date.rb +12 -0
  43. data/lib/ardm/property/datetime.rb +12 -0
  44. data/lib/ardm/property/decimal.rb +38 -0
  45. data/lib/ardm/property/discriminator.rb +65 -0
  46. data/lib/ardm/property/enum.rb +51 -0
  47. data/lib/ardm/property/epoch_time.rb +38 -0
  48. data/lib/ardm/property/file_path.rb +25 -0
  49. data/lib/ardm/property/flag.rb +65 -0
  50. data/lib/ardm/property/float.rb +18 -0
  51. data/lib/ardm/property/integer.rb +24 -0
  52. data/lib/ardm/property/invalid_value_error.rb +22 -0
  53. data/lib/ardm/property/ip_address.rb +35 -0
  54. data/lib/ardm/property/json.rb +49 -0
  55. data/lib/ardm/property/lookup.rb +29 -0
  56. data/lib/ardm/property/numeric.rb +40 -0
  57. data/lib/ardm/property/object.rb +36 -0
  58. data/lib/ardm/property/paranoid_boolean.rb +18 -0
  59. data/lib/ardm/property/paranoid_datetime.rb +17 -0
  60. data/lib/ardm/property/regexp.rb +22 -0
  61. data/lib/ardm/property/serial.rb +16 -0
  62. data/lib/ardm/property/slug.rb +29 -0
  63. data/lib/ardm/property/string.rb +40 -0
  64. data/lib/ardm/property/support/dirty_minder.rb +169 -0
  65. data/lib/ardm/property/support/flags.rb +41 -0
  66. data/lib/ardm/property/support/paranoid_base.rb +78 -0
  67. data/lib/ardm/property/text.rb +11 -0
  68. data/lib/ardm/property/time.rb +12 -0
  69. data/lib/ardm/property/uri.rb +27 -0
  70. data/lib/ardm/property/uuid.rb +65 -0
  71. data/lib/ardm/property/validation.rb +208 -0
  72. data/lib/ardm/property/yaml.rb +38 -0
  73. data/lib/ardm/property.rb +891 -0
  74. data/lib/ardm/property_set.rb +152 -0
  75. data/lib/ardm/query/expression.rb +85 -0
  76. data/lib/ardm/query/ext/symbol.rb +37 -0
  77. data/lib/ardm/query/operator.rb +64 -0
  78. data/lib/ardm/record.rb +1 -0
  79. data/lib/ardm/support/assertions.rb +8 -0
  80. data/lib/ardm/support/deprecate.rb +12 -0
  81. data/lib/ardm/support/descendant_set.rb +89 -0
  82. data/lib/ardm/support/equalizer.rb +48 -0
  83. data/lib/ardm/support/ext/array.rb +22 -0
  84. data/lib/ardm/support/ext/blank.rb +25 -0
  85. data/lib/ardm/support/ext/hash.rb +67 -0
  86. data/lib/ardm/support/ext/module.rb +47 -0
  87. data/lib/ardm/support/ext/object.rb +57 -0
  88. data/lib/ardm/support/ext/string.rb +24 -0
  89. data/lib/ardm/support/ext/try_dup.rb +12 -0
  90. data/lib/ardm/support/hook.rb +405 -0
  91. data/lib/ardm/support/lazy_array.rb +451 -0
  92. data/lib/ardm/support/local_object_space.rb +13 -0
  93. data/lib/ardm/support/logger.rb +201 -0
  94. data/lib/ardm/support/mash.rb +176 -0
  95. data/lib/ardm/support/naming_conventions.rb +90 -0
  96. data/lib/ardm/support/ordered_set.rb +380 -0
  97. data/lib/ardm/support/subject.rb +33 -0
  98. data/lib/ardm/support/subject_set.rb +250 -0
  99. data/lib/ardm/version.rb +3 -0
  100. data/lib/ardm.rb +56 -0
  101. data/spec/fixtures/api_user.rb +11 -0
  102. data/spec/fixtures/article.rb +22 -0
  103. data/spec/fixtures/bookmark.rb +14 -0
  104. data/spec/fixtures/invention.rb +5 -0
  105. data/spec/fixtures/network_node.rb +23 -0
  106. data/spec/fixtures/person.rb +17 -0
  107. data/spec/fixtures/software_package.rb +22 -0
  108. data/spec/fixtures/ticket.rb +12 -0
  109. data/spec/fixtures/tshirt.rb +15 -0
  110. data/spec/integration/api_key_spec.rb +25 -0
  111. data/spec/integration/bcrypt_hash_spec.rb +45 -0
  112. data/spec/integration/comma_separated_list_spec.rb +85 -0
  113. data/spec/integration/dirty_minder_spec.rb +197 -0
  114. data/spec/integration/enum_spec.rb +79 -0
  115. data/spec/integration/epoch_time_spec.rb +59 -0
  116. data/spec/integration/file_path_spec.rb +158 -0
  117. data/spec/integration/flag_spec.rb +72 -0
  118. data/spec/integration/ip_address_spec.rb +151 -0
  119. data/spec/integration/json_spec.rb +70 -0
  120. data/spec/integration/slug_spec.rb +65 -0
  121. data/spec/integration/uri_spec.rb +136 -0
  122. data/spec/integration/uuid_spec.rb +102 -0
  123. data/spec/integration/yaml_spec.rb +85 -0
  124. data/spec/public/property/binary_spec.rb +41 -0
  125. data/spec/public/property/boolean_spec.rb +30 -0
  126. data/spec/public/property/class_spec.rb +28 -0
  127. data/spec/public/property/date_spec.rb +22 -0
  128. data/spec/public/property/date_time_spec.rb +22 -0
  129. data/spec/public/property/decimal_spec.rb +23 -0
  130. data/spec/public/property/discriminator_spec.rb +133 -0
  131. data/spec/public/property/float_spec.rb +22 -0
  132. data/spec/public/property/integer_spec.rb +22 -0
  133. data/spec/public/property/object_spec.rb +103 -0
  134. data/spec/public/property/serial_spec.rb +22 -0
  135. data/spec/public/property/string_spec.rb +22 -0
  136. data/spec/public/property/text_spec.rb +23 -0
  137. data/spec/public/property/time_spec.rb +22 -0
  138. data/spec/public/property_spec.rb +316 -0
  139. data/spec/rcov.opts +6 -0
  140. data/spec/schema.rb +86 -0
  141. data/spec/semipublic/property/binary_spec.rb +14 -0
  142. data/spec/semipublic/property/boolean_spec.rb +48 -0
  143. data/spec/semipublic/property/class_spec.rb +36 -0
  144. data/spec/semipublic/property/date_spec.rb +44 -0
  145. data/spec/semipublic/property/date_time_spec.rb +47 -0
  146. data/spec/semipublic/property/decimal_spec.rb +83 -0
  147. data/spec/semipublic/property/discriminator_spec.rb +22 -0
  148. data/spec/semipublic/property/float_spec.rb +83 -0
  149. data/spec/semipublic/property/integer_spec.rb +83 -0
  150. data/spec/semipublic/property/lookup_spec.rb +27 -0
  151. data/spec/semipublic/property/serial_spec.rb +14 -0
  152. data/spec/semipublic/property/string_spec.rb +14 -0
  153. data/spec/semipublic/property/text_spec.rb +30 -0
  154. data/spec/semipublic/property/time_spec.rb +49 -0
  155. data/spec/semipublic/property_spec.rb +51 -0
  156. data/spec/shared/flags_shared_spec.rb +36 -0
  157. data/spec/shared/identity_function_group.rb +5 -0
  158. data/spec/shared/public_property_spec.rb +229 -0
  159. data/spec/shared/semipublic_property_spec.rb +159 -0
  160. data/spec/spec.opts +4 -0
  161. data/spec/spec_helper.rb +58 -0
  162. data/spec/unit/bcrypt_hash_spec.rb +154 -0
  163. data/spec/unit/csv_spec.rb +139 -0
  164. data/spec/unit/dirty_minder_spec.rb +64 -0
  165. data/spec/unit/enum_spec.rb +125 -0
  166. data/spec/unit/epoch_time_spec.rb +72 -0
  167. data/spec/unit/file_path_spec.rb +75 -0
  168. data/spec/unit/flag_spec.rb +114 -0
  169. data/spec/unit/ip_address_spec.rb +109 -0
  170. data/spec/unit/json_spec.rb +127 -0
  171. data/spec/unit/paranoid_boolean_spec.rb +142 -0
  172. data/spec/unit/paranoid_datetime_spec.rb +149 -0
  173. data/spec/unit/regexp_spec.rb +62 -0
  174. data/spec/unit/uri_spec.rb +64 -0
  175. data/spec/unit/yaml_spec.rb +111 -0
  176. data/tasks/spec.rake +40 -0
  177. data/tasks/yard.rake +9 -0
  178. data/tasks/yardstick.rake +19 -0
  179. metadata +350 -0
@@ -0,0 +1,176 @@
1
+ module Ardm
2
+ # This class has dubious semantics and we only have it so that people can write
3
+ # params[:key] instead of params['key'].
4
+ class Mash < Hash
5
+
6
+ # Initializes a new mash.
7
+ #
8
+ # @param [Hash, Object] constructor
9
+ # The default value for the mash. If +constructor+ is a Hash, a new mash
10
+ # will be created based on the keys of the hash and no default value will
11
+ # be set.
12
+ def initialize(constructor = {})
13
+ if constructor.is_a?(Hash)
14
+ super()
15
+ update(constructor)
16
+ else
17
+ super(constructor)
18
+ end
19
+ end
20
+
21
+ # Gets the default value for the mash.
22
+ #
23
+ # @param [Object] key
24
+ # The default value for the mash. If +key+ is a Symbol and it is a key in
25
+ # the mash, then the default value will be set to the value matching the
26
+ # key.
27
+ def default(key = nil)
28
+ if key.is_a?(Symbol) && include?(key = key.to_s)
29
+ self[key]
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
36
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
37
+
38
+ # Sets the +value+ associated with the specified +key+.
39
+ #
40
+ # @param [Object] key The key to set.
41
+ # @param [Object] value The value to set the key to.
42
+ def []=(key, value)
43
+ regular_writer(convert_key(key), convert_value(value))
44
+ end
45
+
46
+ # Updates the mash with the key/value pairs from the specified hash.
47
+ #
48
+ # @param [Hash] other_hash
49
+ # A hash to update values in the mash with. The keys and the values will be
50
+ # converted to Mash format.
51
+ #
52
+ # @return [Mash] The updated mash.
53
+ def update(other_hash)
54
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
55
+ self
56
+ end
57
+
58
+ alias_method :merge!, :update
59
+
60
+ # Determines whether the mash contains the specified +key+.
61
+ #
62
+ # @param [Object] key The key to check for.
63
+ # @return [Boolean] True if the key exists in the mash.
64
+ def key?(key)
65
+ super(convert_key(key))
66
+ end
67
+
68
+ alias_method :include?, :key?
69
+ alias_method :has_key?, :key?
70
+ alias_method :member?, :key?
71
+
72
+ # @param [Object] key The key to fetch.
73
+ # @param [Array] *extras Default value.
74
+ #
75
+ # @return [Object] The value at key or the default value.
76
+ def fetch(key, *extras)
77
+ super(convert_key(key), *extras)
78
+ end
79
+
80
+ # @param [Array] *indices
81
+ # The keys to retrieve values for.
82
+ #
83
+ # @return [Array] The values at each of the provided keys.
84
+ def values_at(*indices)
85
+ indices.collect {|key| self[convert_key(key)]}
86
+ end
87
+
88
+ # @param [Hash] hash The hash to merge with the mash.
89
+ #
90
+ # @return [Mash] A new mash with the hash values merged in.
91
+ def merge(hash)
92
+ self.dup.update(hash)
93
+ end
94
+
95
+ # @param [Object] key The key to delete from the mash.
96
+ def delete(key)
97
+ super(convert_key(key))
98
+ end
99
+
100
+ # Returns a mash that includes everything but the given +keys+.
101
+ #
102
+ # @param [Array<String, Symbol>] *keys The mash keys to exclude.
103
+ #
104
+ # @return [Mash] A new mash without the selected keys.
105
+ #
106
+ # @example
107
+ # { :one => 1, :two => 2, :three => 3 }.except(:one)
108
+ # #=> { "two" => 2, "three" => 3 }
109
+ def except(*keys)
110
+ self.dup.except!(*keys.map {|k| convert_key(k)})
111
+ end
112
+
113
+ # Removes the specified +keys+ from the mash.
114
+ #
115
+ # @param [Array] *keys The mash keys to exclude.
116
+ #
117
+ # @return [Hash] +hash+
118
+ #
119
+ # @example
120
+ # mash = { :one => 1, :two => 2, :three => 3 }
121
+ # mash.except!(:one, :two)
122
+ # mash # => { :three => 3 }
123
+ def except!(*keys)
124
+ keys.each { |key| delete(key) }
125
+ self
126
+ end
127
+
128
+ # Used to provide the same interface as Hash.
129
+ #
130
+ # @return [Mash] This mash unchanged.
131
+ def stringify_keys!; self end
132
+
133
+ # @return [Hash] The mash as a Hash with symbolized keys.
134
+ def symbolize_keys
135
+ h = Hash.new(default)
136
+ each { |key, val| h[key.to_sym] = val }
137
+ h
138
+ end
139
+
140
+ # @return [Hash] The mash as a Hash with string keys.
141
+ def to_hash
142
+ Hash.new(default).merge(self)
143
+ end
144
+
145
+ protected
146
+ # @param [Object] key The key to convert.
147
+ #
148
+ # @param [Object]
149
+ # The converted key. If the key was a symbol, it will be converted to a
150
+ # string.
151
+ #
152
+ # @api private
153
+ def convert_key(key)
154
+ key.kind_of?(Symbol) ? key.to_s : key
155
+ end
156
+
157
+ # @param [Object] value The value to convert.
158
+ #
159
+ # @return [Object]
160
+ # The converted value. A Hash or an Array of hashes, will be converted to
161
+ # their Mash equivalents.
162
+ #
163
+ # @api private
164
+ def convert_value(value)
165
+ if value.class == Hash
166
+ mash = Mash.new(value)
167
+ mash.default = value.default
168
+ mash
169
+ elsif value.is_a?(Array)
170
+ value.collect { |e| convert_value(e) }
171
+ else
172
+ value
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,90 @@
1
+ module Ardm
2
+
3
+ # Use these modules to establish naming conventions.
4
+ # The default is UnderscoredAndPluralized.
5
+ # You assign a naming convention like so:
6
+ #
7
+ # connection.adapter.resource_naming_convention = NamingConventions::Resource::Underscored
8
+ #
9
+ # You can also easily assign a custom convention with a Proc:
10
+ #
11
+ # connection.adapter.resource_naming_convention = lambda do |value|
12
+ # 'tbl' + value.camelize(true)
13
+ # end
14
+ #
15
+ # Or by simply defining your own module in NamingConventions that responds to
16
+ # ::call.
17
+ #
18
+ # NOTE: It's important to set the convention before accessing your models
19
+ # since the resource_names are cached after first accessed.
20
+ # Ardm.setup(name, uri) returns the Adapter for convenience, so you can
21
+ # use code like this:
22
+ #
23
+ # adapter = Ardm.setup(:default, 'mock://localhost/mock')
24
+ # adapter.resource_naming_convention = NamingConventions::Resource::Underscored
25
+ module NamingConventions
26
+
27
+ module Resource
28
+
29
+ module UnderscoredAndPluralized
30
+ def self.call(name)
31
+ Ardm::Inflector.pluralize(Ardm::Inflector.underscore(name)).gsub('/', '_')
32
+ end
33
+ end # module UnderscoredAndPluralized
34
+
35
+ module UnderscoredAndPluralizedWithoutModule
36
+ def self.call(name)
37
+ Ardm::Inflector.pluralize(Ardm::Inflector.underscore(Ardm::Inflector.demodulize(name)))
38
+ end
39
+ end # module UnderscoredAndPluralizedWithoutModule
40
+
41
+ module UnderscoredAndPluralizedWithoutLeadingModule
42
+ def self.call(name)
43
+ UnderscoredAndPluralized.call(name.to_s.gsub(/^[^:]*::/,''))
44
+ end
45
+ end
46
+
47
+ module Underscored
48
+ def self.call(name)
49
+ Ardm::Inflector.underscore(name)
50
+ end
51
+ end # module Underscored
52
+
53
+ module Yaml
54
+ def self.call(name)
55
+ "#{Ardm::Inflector.pluralize(Ardm::Inflector.underscore(name))}.yaml"
56
+ end
57
+ end # module Yaml
58
+
59
+ end # module Resource
60
+
61
+ module Field
62
+
63
+ module UnderscoredAndPluralized
64
+ def self.call(property)
65
+ Ardm::Inflector.pluralize(Ardm::Inflector.underscore(property.name.to_s)).gsub('/', '_')
66
+ end
67
+ end # module UnderscoredAndPluralized
68
+
69
+ module UnderscoredAndPluralizedWithoutModule
70
+ def self.call(property)
71
+ Ardm::Inflector.pluralize(Ardm::Inflector.underscore(Ardm::Inflector.demodulize(property.name.to_s)))
72
+ end
73
+ end # module UnderscoredAndPluralizedWithoutModule
74
+
75
+ module Underscored
76
+ def self.call(property)
77
+ Ardm::Inflector.underscore(property.name.to_s)
78
+ end
79
+ end # module Underscored
80
+
81
+ module Yaml
82
+ def self.call(property)
83
+ "#{Ardm::Inflector.pluralize(Ardm::Inflector.underscore(property.name.to_s))}.yaml"
84
+ end
85
+ end # module Yaml
86
+
87
+ end # module Field
88
+
89
+ end # module NamingConventions
90
+ end # module Ardm
@@ -0,0 +1,380 @@
1
+ module Ardm
2
+
3
+ # An ordered set of things
4
+ #
5
+ # {OrderedSet} implements set behavior and keeps
6
+ # track of the order in which entries were added.
7
+ #
8
+ # {OrderedSet} allows to inject a class that implements
9
+ # {OrderedSet::Cache::API} at construction time, and will
10
+ # use that cache implementation to enforce set semantics
11
+ # and perform internal caching of insertion order.
12
+ #
13
+ # @see OrderedSet::Cache::API
14
+ # @see OrderedSet::Cache
15
+ # @see SubjectSet::NameCache
16
+ #
17
+ # @api private
18
+ class OrderedSet
19
+
20
+ # The default cache used by {OrderedSet}
21
+ #
22
+ # Uses a {Hash} as internal storage and enforces set semantics
23
+ # by calling #eql? and #hash on the set's entries.
24
+ #
25
+ # @api private
26
+ class Cache
27
+
28
+ # The default implementation of the {API} that {OrderedSet} expects from
29
+ # the cache object that it uses to
30
+ #
31
+ # 1. keep track of insertion order
32
+ # 2. enforce set semantics.
33
+ #
34
+ # Classes including {API} must customize the behavior of the cache in 2 ways:
35
+ #
36
+ # They must determine the value to use as cache key and thus set discriminator,
37
+ # by implementing the {#key_for} method. The {#key_for} method accepts an arbitrary
38
+ # object as param and the method is free to return whatever value from that method.
39
+ # Obviously this will most likely be some attribute or value otherwise derived from
40
+ # the object that got passed in.
41
+ #
42
+ # They must determine which objects are valid set entries by overwriting the
43
+ # {#valid?} method. The {#valid?} method accepts an arbitrary object as param and
44
+ # the overwriting method must return either true or false.
45
+ #
46
+ # The motivation behind this is that set semantics cannot always be enforced
47
+ # by calling {#eql?} and {#hash} on the set's entries. For example, two entries
48
+ # might be considered unique wrt the set if their names are the same, but other
49
+ # internal state differs. This is exactly the case for {Ardm::Property} and
50
+ # {Ardm::Associations::Relationship} objects.
51
+ #
52
+ # @see Ardm::SubjectSet::NameCache
53
+ #
54
+ # @api private
55
+ module API
56
+
57
+ # Initialize a new Cache
58
+ #
59
+ # @api private
60
+ def initialize
61
+ @cache = {}
62
+ end
63
+
64
+ # Tests if the given entry qualifies to be added to the cache
65
+ #
66
+ # @param [Object] entry
67
+ # the entry to be checked
68
+ #
69
+ # @return [Boolean]
70
+ # true if the entry qualifies to be added to the cache
71
+ #
72
+ # @api private
73
+ def valid?(entry)
74
+ raise NotImplementedError, "#{self}#valid? must be implemented"
75
+ end
76
+
77
+ # Given an entry, return the key to be used in the cache
78
+ #
79
+ # @param [Object] entry
80
+ # the entry to get the key for
81
+ #
82
+ # @return [Object, nil]
83
+ # a value derived from the entry that is used as key in the cache
84
+ #
85
+ # @api private
86
+ def key_for(entry)
87
+ raise NotImplementedError, "#{self}#key_for must be implemented"
88
+ end
89
+
90
+ # Check if the entry exists in the cache
91
+ #
92
+ # @param [Object] entry
93
+ # the entry to test for
94
+ #
95
+ # @return [Boolean]
96
+ # true if entry is included in the cache
97
+ #
98
+ # @api private
99
+ def include?(entry)
100
+ @cache.has_key?(key_for(entry))
101
+ end
102
+
103
+ # Return the index for the entry in the cache
104
+ #
105
+ # @param [Object] entry
106
+ # the entry to get the index for
107
+ #
108
+ # @return [Integer, nil]
109
+ # the index for the entry, or nil if it does not exist
110
+ #
111
+ # @api private
112
+ def [](entry)
113
+ @cache[key_for(entry)]
114
+ end
115
+
116
+ # Set the index for the entry in the cache
117
+ #
118
+ # @param [Object] entry
119
+ # the entry to set the index for
120
+ # @param [Integer] index
121
+ # the index to assign to the given entry
122
+ #
123
+ # @return [Integer]
124
+ # the given index for the entry
125
+ #
126
+ # @api private
127
+ def []=(entry, index)
128
+ if valid?(entry)
129
+ @cache[key_for(entry)] = index
130
+ end
131
+ end
132
+
133
+ # Delete an entry from the cache
134
+ #
135
+ # @param [Object] entry
136
+ # the entry to delete from the cache
137
+ #
138
+ # @return [API] self
139
+ #
140
+ # @api private
141
+ def delete(entry)
142
+ deleted_index = @cache.delete(key_for(entry))
143
+ if deleted_index
144
+ @cache.each do |key, index|
145
+ @cache[key] -= 1 if index > deleted_index
146
+ end
147
+ end
148
+ deleted_index
149
+ end
150
+
151
+ # Removes all entries and returns self
152
+ #
153
+ # @return [API] self
154
+ #
155
+ # @api private
156
+ def clear
157
+ @cache.clear
158
+ self
159
+ end
160
+
161
+ end # module API
162
+
163
+ include API
164
+
165
+ # Tests if the given entry qualifies to be added to the cache
166
+ #
167
+ # @param [Object] entry
168
+ # the entry to be checked
169
+ #
170
+ # @return [true] true
171
+ #
172
+ # @api private
173
+ def valid?(entry)
174
+ true
175
+ end
176
+
177
+ # Given an entry, return the key to be used in the cache
178
+ #
179
+ # @param [Object] entry
180
+ # the entry to get the key for
181
+ #
182
+ # @return [Object]
183
+ # the passed in entry
184
+ #
185
+ # @api private
186
+ def key_for(entry)
187
+ entry
188
+ end
189
+
190
+ end # class Cache
191
+
192
+ include Enumerable
193
+ extend Equalizer
194
+
195
+ # This set's entries
196
+ #
197
+ # The order in this Array is not guaranteed
198
+ # to be the order in which the entries were
199
+ # inserted. Use #each to access the entries
200
+ # in insertion order.
201
+ #
202
+ # @return [Array]
203
+ # this set's entries
204
+ #
205
+ # @api private
206
+ attr_reader :entries
207
+
208
+ equalize :entries
209
+
210
+ # Initialize an OrderedSet
211
+ #
212
+ # @param [#each] entries
213
+ # the entries to initialize this set with
214
+ # @param [Class<Cache::API>] cache
215
+ # the cache implementation to use
216
+ #
217
+ # @api private
218
+ def initialize(entries = [], cache = Cache)
219
+ @cache = cache.new
220
+ @entries = []
221
+ merge(entries.to_ary)
222
+ end
223
+
224
+ # Initialize a copy of OrderedSet
225
+ #
226
+ # @api private
227
+ def initialize_copy(*)
228
+ @cache = @cache.dup
229
+ @entries = @entries.dup
230
+ end
231
+
232
+ # Get the entry at the given index
233
+ #
234
+ # @param [Integer] index
235
+ # the index of the desired entry
236
+ #
237
+ # @return [Object, nil]
238
+ # the entry at the given index, or nil if no entry is present
239
+ #
240
+ # @api private
241
+ def [](index)
242
+ entries[index]
243
+ end
244
+
245
+ # Add or update an entry in the set
246
+ #
247
+ # If the entry to add isn't part of the set already,
248
+ # it will be added. If an entry with the same cache
249
+ # key as the entry to add is part of the set already,
250
+ # it will be replaced with the given entry.
251
+ #
252
+ # @param [Object] entry
253
+ # the entry to be added
254
+ #
255
+ # @return [OrderedSet] self
256
+ #
257
+ # @api private
258
+ def <<(entry)
259
+ if index = @cache[entry]
260
+ entries[index] = entry
261
+ else
262
+ @cache[entry] = size
263
+ entries << entry
264
+ end
265
+ self
266
+ end
267
+
268
+ # Merge with another Enumerable object
269
+ #
270
+ # @param [#each] other
271
+ # the Enumerable to merge with this OrderedSet
272
+ #
273
+ # @return [OrderedSet] self
274
+ #
275
+ # @api private
276
+ def merge(other)
277
+ other.each { |entry| self << entry }
278
+ self
279
+ end
280
+
281
+ # Delete an entry from this OrderedSet
282
+ #
283
+ # @param [Object] entry
284
+ # the entry to delete
285
+ #
286
+ # @return [Object, nil]
287
+ # the deleted entry or nil
288
+ #
289
+ # @api private
290
+ def delete(entry)
291
+ if index = @cache.delete(entry)
292
+ entries.delete_at(index)
293
+ end
294
+ end
295
+
296
+ # Removes all entries and returns self
297
+ #
298
+ # @return [OrderedSet] self
299
+ #
300
+ # @api private
301
+ def clear
302
+ @cache.clear
303
+ entries.clear
304
+ self
305
+ end
306
+
307
+ # Iterate over each entry in the set
308
+ #
309
+ # @yield [entry]
310
+ # all entries in the set
311
+ #
312
+ # @yieldparam [Object] entry
313
+ # an entry in the set
314
+ #
315
+ # @return [OrderedSet] self
316
+ #
317
+ # @api private
318
+ def each
319
+ entries.each { |entry| yield(entry) }
320
+ self
321
+ end
322
+
323
+ # The number of entries
324
+ #
325
+ # @return [Integer]
326
+ # the number of entries
327
+ #
328
+ # @api private
329
+ def size
330
+ entries.size
331
+ end
332
+
333
+ # Check if there are any entries
334
+ #
335
+ # @return [Boolean]
336
+ # true if the set is empty
337
+ #
338
+ # @api private
339
+ def empty?
340
+ entries.empty?
341
+ end
342
+
343
+ # Check if the entry exists in the set
344
+ #
345
+ # @param [Object] entry
346
+ # the entry to test for
347
+ #
348
+ # @return [Boolean]
349
+ # true if entry is included in the set
350
+ #
351
+ # @api private
352
+ def include?(entry)
353
+ entries.include?(entry)
354
+ end
355
+
356
+ # Return the index for the entry in the set
357
+ #
358
+ # @param [Object] entry
359
+ # the entry to check the set for
360
+ #
361
+ # @return [Integer, nil]
362
+ # the index for the entry, or nil if it does not exist
363
+ #
364
+ # @api private
365
+ def index(entry)
366
+ @cache[entry]
367
+ end
368
+
369
+ # Convert the OrderedSet into an Array
370
+ #
371
+ # @return [Array]
372
+ # an array containing all the OrderedSet's entries
373
+ #
374
+ # @api private
375
+ def to_ary
376
+ entries
377
+ end
378
+
379
+ end # class OrderedSet
380
+ end # module Ardm
@@ -0,0 +1,33 @@
1
+ module Ardm
2
+ module Subject
3
+ # Returns a default value of the subject for given resource
4
+ #
5
+ # When default value is a callable object, it is called with resource
6
+ # and subject passed as arguments.
7
+ #
8
+ # @param [Resource] resource
9
+ # the model instance for which the default is to be set
10
+ #
11
+ # @return [Object]
12
+ # the default value of this subject for +resource+
13
+ #
14
+ # @api semipublic
15
+ def default_for(resource)
16
+ if @default.respond_to?(:call)
17
+ @default.call(resource, self)
18
+ else
19
+ Ardm::Ext.try_dup(@default)
20
+ end
21
+ end
22
+
23
+ # Returns true if the subject has a default value
24
+ #
25
+ # @return [Boolean]
26
+ # true if the subject has a default value
27
+ #
28
+ # @api semipublic
29
+ def default?
30
+ @options.key?(:default)
31
+ end
32
+ end
33
+ end