Class: ActiveCabinet
- Inherits:
-
Object
- Object
- ActiveCabinet
- Extended by:
- Forwardable
- Defined in:
- lib/active_cabinet.rb,
lib/active_cabinet/config.rb,
lib/active_cabinet/version.rb,
lib/active_cabinet/metaclass.rb
Overview
ActiveCabinet lets you create a HashCabinet
collection model by subclassing ActiveCabinet:
class Song < ActiveCabinet
end
Now, you can perform CRUD operations on this collection, which will be persisted to disk:
# Create
Song.create id: 1, title: 'Moonchild', artist: 'Iron Maiden'
# Read
moonchild = Song[1] # or Song.find 1
# Update
moonchild.title = "22 Acacia Avenue"
moonchild.save
# or
moonchild.update! title: "22 Acacia Avenue"
# Delete
Song.delete 1
Defined Under Namespace
Classes: Config
Constant Summary collapse
- VERSION =
"0.2.4"
Instance Attribute Summary collapse
-
#attributes ⇒ Hash
readonly
The attributes of the record.
-
#error ⇒ String?
readonly
The last validation error, after calling #valid?.
Constructor collapse
-
#initialize(attributes = {}) ⇒ ActiveCabinet
constructor
Initializes a new record with #attributes.
Attribute Management collapse
-
.allowed_attributes ⇒ Array<Symbol>
Returns an array containing the keys of all allowed attributes as defined by ActiveCabinet.required_attributes, ActiveCabinet.optional_attributes and ActiveCabinet.default_attributes.
-
.default_attributes(args = nil) ⇒ Hash<Symbol, Object>
Sets the default record attribute values.
-
.optional_attributes(*args) ⇒ Array<Symbol>
Sets the optional record attribute names.
-
.required_attributes(*args) ⇒ Array<Symbol>
Sets the required record attribute names.
-
#allowed_attributes ⇒ Array<Symbol>
Returns an array containing ActiveCabinet.required_attributes and ActiveCabinet.optional_attributes.
- #default_attributes ⇒ Object
-
#optional_attributes ⇒ Array<Symbol>
Returns an array of optional record attributes.
-
#required_attributes ⇒ Array<Symbol>
Returns an array of required record attributes.
-
#valid? ⇒ Boolean
Returns
true
if the object is valid.
Attribute Accessors collapse
-
#[](key) ⇒ Object
Returns the attribute value for the given key.
-
#[]=(key, value) ⇒ Object
Sets the attribute value for the given key.
Dynamic Attribute Accessors collapse
-
#method_missing(method_name, *args, &blk) ⇒ Object
Provides read/write access to #attributes.
-
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
Returns
true
when calling#respond_to?
with an attribute name.
Loading and Saving collapse
-
#reload ⇒ self?
Reads the attributes of the record from the cabinet and returns the record itself.
-
#save ⇒ self, false
Saves the record to the cabinet if it is valid.
-
#saved? ⇒ Boolean
Returns
true
if the record exists in the cabinet. -
#update(new_attributes) ⇒ Object
Update the record with new or modified attributes.
-
#update!(new_attributes) ⇒ Object
Update the record with new or modified attributes, and save.
Utilities collapse
-
.cabinet ⇒ HashCabinet
Returns the
HashCabinet
instance. -
.cabinet_name(new_name = nil) ⇒ String
Returns or sets the cabinet name.
-
.to_h ⇒ Object
Returns all records as a hash, with record IDs as the keys.
-
#to_h ⇒ Hash<Symbol, Object>
Returns a Hash of attributes.
Creating Records collapse
-
.[]=(id, attributes) ⇒ Object
Creates and saves a new record instance.
-
.create(attributes) ⇒ Object
Creates and saves a new record instance.
Reading Records collapse
-
.all ⇒ Array
Returns all records.
-
.all_attributes ⇒ Array
Returns all records, as an Array of Hashes.
-
.each {|record| ... } ⇒ Object
Yields each record to the given block.
-
.find(id) ⇒ Object?
(also: [])
Returns the record matching the
id
. -
.first ⇒ Object
Returns the first record.
-
.last ⇒ Object
Returns the last record.
-
.random ⇒ Object
Returns a random racord.
-
.where(query = nil) {|record| ... } ⇒ Array<Object>
Returns an array of records for which the block returns true.
Deleting Records collapse
-
.delete(id) ⇒ Boolean
Deletes a record matching the
id
. -
.delete_if ⇒ Object
Deletes a record for which the block returns true.
-
.drop ⇒ Object
Deletes all records.
Constructor Details
#initialize(attributes = {}) ⇒ ActiveCabinet
Initializes a new record with #attributes
16 17 18 |
# File 'lib/active_cabinet.rb', line 16 def initialize(attributes = {}) @attributes = default_attributes.merge attributes.transform_keys(&:to_sym) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &blk) ⇒ Object
Provides read/write access to #attributes
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/active_cabinet.rb', line 90 def method_missing(method_name, *args, &blk) name = method_name return attributes[name] if attributes.has_key? name suffix = nil if name.to_s.end_with?('=', '?') suffix = name[-1] name = name[0..-2].to_sym end case suffix when "=" attributes[name] = args.first when "?" !!attributes[name] else super end end |
Instance Attribute Details
#attributes ⇒ Hash (readonly)
Returns the attributes of the record.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/active_cabinet.rb', line 8 class ActiveCabinet attr_reader :attributes, :error # @!group Constructor # Initializes a new record with {attributes} # # @param [Hash] attributes record attributes def initialize(attributes = {}) @attributes = default_attributes.merge attributes.transform_keys(&:to_sym) end # @!group Attribute Management # Returns an array containing {required_attributes} and {optional_attributes}. # # @return [Array<Symbol>] array of required attribute keys. def allowed_attributes self.class.allowed_attributes end # Returns an array of required record attributes. # # @see ActiveCabinet.required_attributes. # @return [Array<Symbol>] the array of required attributes def required_attributes self.class.required_attributes end # Returns an array of optional record attributes. # # @see ActiveCabinet.optional_attributes. # @return [Array<Symbol>] the array of optional attributes def optional_attributes self.class.optional_attributes end def default_attributes self.class.default_attributes end # Returns +true+ if the object is valid. # # @return [Boolean] +true+ if the record is valid. def valid? missing_keys = required_attributes - attributes.keys if missing_keys.any? @error = "missing required attributes: #{missing_keys}" return false end if !optional_attributes or optional_attributes.any? invalid_keys = attributes.keys - allowed_attributes if invalid_keys.any? @error = "invalid attributes: #{invalid_keys}" return false end end true end # @!group Attribute Accessors # Returns the attribute value for the given key. # # @return [Object] the attribute value. def [](key) attributes[key] end # Sets the attribute value for the given key. # # @param [Symbol] key the attribute key. # @param [Object] value the attribute value. def []=(key, value) attributes[key] = value end # @!group Dynamic Attribute Accessors # Provides read/write access to {attributes} def method_missing(method_name, *args, &blk) name = method_name return attributes[name] if attributes.has_key? name suffix = nil if name.to_s.end_with?('=', '?') suffix = name[-1] name = name[0..-2].to_sym end case suffix when "=" attributes[name] = args.first when "?" !!attributes[name] else super end end # Returns +true+ when calling +#respond_to?+ with an attribute name. # # @return [Boolean] +true+ if there is a matching attribute. def respond_to_missing?(method_name, include_private = false) name = method_name name = name[0..-2].to_sym if name.to_s.end_with?('=', '?') attributes.has_key?(name) || super end # @!group Loading and Saving # Reads the attributes of the record from the cabinet and returns the # record itself. If the record is not stored on disk, returns +nil+. # # @return [self, nil] the object or +nil+ if the object is not stored. def reload return nil unless saved? update cabinet[id] self end # Saves the record to the cabinet if it is valid. Returns the record on # success, or +false+ on failure. # # @return [self, false] the record or +false+ on failure. def save if valid? cabinet[id] = attributes self else false end end # Returns +true+ if the record exists in the cabinet. # # @note This method only verifies that the ID of the record exists. The # attributes of the instance and the stored record may differ. # # @return [Boolean] +true+ if the record is saved in the cabinet. def saved? cabinet.key? id end # Update the record with new or modified attributes. # # @param [Hash] new_attributes record attributes def update(new_attributes) @attributes = attributes.merge(new_attributes.transform_keys &:to_sym) end # Update the record with new or modified attributes, and save. # # @param [Hash] new_attributes record attributes def update!(new_attributes) update new_attributes save end # @!group Utilities # Returns a Hash of attributes # # @return [Hash<Symbol, Object>] the hash of attriibutes/ def to_h attributes end protected def cabinet self.class.cabinet end end |
#error ⇒ String? (readonly)
Returns the last validation error, after calling #valid?.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/active_cabinet.rb', line 8 class ActiveCabinet attr_reader :attributes, :error # @!group Constructor # Initializes a new record with {attributes} # # @param [Hash] attributes record attributes def initialize(attributes = {}) @attributes = default_attributes.merge attributes.transform_keys(&:to_sym) end # @!group Attribute Management # Returns an array containing {required_attributes} and {optional_attributes}. # # @return [Array<Symbol>] array of required attribute keys. def allowed_attributes self.class.allowed_attributes end # Returns an array of required record attributes. # # @see ActiveCabinet.required_attributes. # @return [Array<Symbol>] the array of required attributes def required_attributes self.class.required_attributes end # Returns an array of optional record attributes. # # @see ActiveCabinet.optional_attributes. # @return [Array<Symbol>] the array of optional attributes def optional_attributes self.class.optional_attributes end def default_attributes self.class.default_attributes end # Returns +true+ if the object is valid. # # @return [Boolean] +true+ if the record is valid. def valid? missing_keys = required_attributes - attributes.keys if missing_keys.any? @error = "missing required attributes: #{missing_keys}" return false end if !optional_attributes or optional_attributes.any? invalid_keys = attributes.keys - allowed_attributes if invalid_keys.any? @error = "invalid attributes: #{invalid_keys}" return false end end true end # @!group Attribute Accessors # Returns the attribute value for the given key. # # @return [Object] the attribute value. def [](key) attributes[key] end # Sets the attribute value for the given key. # # @param [Symbol] key the attribute key. # @param [Object] value the attribute value. def []=(key, value) attributes[key] = value end # @!group Dynamic Attribute Accessors # Provides read/write access to {attributes} def method_missing(method_name, *args, &blk) name = method_name return attributes[name] if attributes.has_key? name suffix = nil if name.to_s.end_with?('=', '?') suffix = name[-1] name = name[0..-2].to_sym end case suffix when "=" attributes[name] = args.first when "?" !!attributes[name] else super end end # Returns +true+ when calling +#respond_to?+ with an attribute name. # # @return [Boolean] +true+ if there is a matching attribute. def respond_to_missing?(method_name, include_private = false) name = method_name name = name[0..-2].to_sym if name.to_s.end_with?('=', '?') attributes.has_key?(name) || super end # @!group Loading and Saving # Reads the attributes of the record from the cabinet and returns the # record itself. If the record is not stored on disk, returns +nil+. # # @return [self, nil] the object or +nil+ if the object is not stored. def reload return nil unless saved? update cabinet[id] self end # Saves the record to the cabinet if it is valid. Returns the record on # success, or +false+ on failure. # # @return [self, false] the record or +false+ on failure. def save if valid? cabinet[id] = attributes self else false end end # Returns +true+ if the record exists in the cabinet. # # @note This method only verifies that the ID of the record exists. The # attributes of the instance and the stored record may differ. # # @return [Boolean] +true+ if the record is saved in the cabinet. def saved? cabinet.key? id end # Update the record with new or modified attributes. # # @param [Hash] new_attributes record attributes def update(new_attributes) @attributes = attributes.merge(new_attributes.transform_keys &:to_sym) end # Update the record with new or modified attributes, and save. # # @param [Hash] new_attributes record attributes def update!(new_attributes) update new_attributes save end # @!group Utilities # Returns a Hash of attributes # # @return [Hash<Symbol, Object>] the hash of attriibutes/ def to_h attributes end protected def cabinet self.class.cabinet end end |
Class Method Details
.[]=(id, attributes) ⇒ Object
Creates and saves a new record instance.
40 41 42 |
# File 'lib/active_cabinet/metaclass.rb', line 40 def []=(id, attributes) create attributes.merge(id: id) end |
.all ⇒ Array
Returns all records.
58 59 60 61 62 |
# File 'lib/active_cabinet/metaclass.rb', line 58 def all cabinet.values.map do |attributes| new(attributes) end end |
.all_attributes ⇒ Array
Returns all records, as an Array of Hashes.
67 68 69 |
# File 'lib/active_cabinet/metaclass.rb', line 67 def all_attributes cabinet.values end |
.allowed_attributes ⇒ Array<Symbol>
Returns an array containing the keys of all allowed attributes as defined by required_attributes, optional_attributes and default_attributes.
175 176 177 |
# File 'lib/active_cabinet/metaclass.rb', line 175 def allowed_attributes (optional_attributes || []) + required_attributes + default_attributes.keys end |
.cabinet ⇒ HashCabinet
Returns the HashCabinet
instance.
231 232 233 |
# File 'lib/active_cabinet/metaclass.rb', line 231 def cabinet @cabinet ||= HashCabinet.new "#{Config.dir}/#{cabinet_name}" end |
.cabinet_name(new_name = nil) ⇒ String
Returns or sets the cabinet name. Defaults to the name of the class, lowercase.
240 241 242 243 244 245 246 247 |
# File 'lib/active_cabinet/metaclass.rb', line 240 def cabinet_name(new_name = nil) if new_name @cabinet = nil @cabinet_name = new_name else @cabinet_name ||= self.to_s.downcase.gsub('::', '_') end end |
.create(attributes) ⇒ Object
Creates and saves a new record instance.
48 49 50 51 |
# File 'lib/active_cabinet/metaclass.rb', line 48 def create(attributes) record = new attributes record.save || record end |
.default_attributes(args = nil) ⇒ Hash<Symbol, Object>
Sets the default record attribute values.
213 214 215 216 217 218 219 |
# File 'lib/active_cabinet/metaclass.rb', line 213 def default_attributes(args = nil) if args @default_attributes = args else @default_attributes ||= {} end end |
.delete(id) ⇒ Boolean
Deletes a record matching the id
.
151 152 153 |
# File 'lib/active_cabinet/metaclass.rb', line 151 def delete(id) !!cabinet.delete(id) end |
.delete_if ⇒ Object
Deletes a record for which the block returns true.
159 160 161 |
# File 'lib/active_cabinet/metaclass.rb', line 159 def delete_if cabinet.delete_if { |key, _value| yield self[key] } end |
.drop ⇒ Object
Deletes all records.
164 165 166 |
# File 'lib/active_cabinet/metaclass.rb', line 164 def drop cabinet.clear end |
.each {|record| ... } ⇒ Object
Yields each record to the given block.
118 119 120 121 122 |
# File 'lib/active_cabinet/metaclass.rb', line 118 def each cabinet.each_value do |attributes| yield new(attributes) end end |
.find(id) ⇒ Object? Also known as: []
Returns the record matching the id
. When providing a Hash with a single key-value pair, it will return the first matching object from the respective where query.
105 106 107 108 109 110 111 112 |
# File 'lib/active_cabinet/metaclass.rb', line 105 def find(id) if id.is_a? Hash where(id).first else attributes = cabinet[id] attributes ? new(attributes) : nil end end |
.first ⇒ Object
Returns the first record.
127 128 129 |
# File 'lib/active_cabinet/metaclass.rb', line 127 def first find keys.first end |
.last ⇒ Object
Returns the last record.
134 135 136 |
# File 'lib/active_cabinet/metaclass.rb', line 134 def last find keys.last end |
.optional_attributes(*args) ⇒ Array<Symbol>
Sets the optional record attribute names.
198 199 200 201 202 203 204 205 206 207 |
# File 'lib/active_cabinet/metaclass.rb', line 198 def optional_attributes(*args) args = args.first if args.first.is_a? Array if args.first === false @optional_attributes = false elsif args.any? @optional_attributes = *args else @optional_attributes.nil? ? [] : @optional_attributes end end |
.random ⇒ Object
Returns a random racord.
141 142 143 |
# File 'lib/active_cabinet/metaclass.rb', line 141 def random find keys.sample end |
.required_attributes(*args) ⇒ Array<Symbol>
Sets the required record attribute names.
183 184 185 186 187 188 189 190 191 192 |
# File 'lib/active_cabinet/metaclass.rb', line 183 def required_attributes(*args) args = args.first if args.first.is_a? Array if args.any? @required_attributes = args @required_attributes.push :id unless @required_attributes.include? :id @required_attributes else @required_attributes ||= [:id] end end |
.to_h ⇒ Object
Returns all records as a hash, with record IDs as the keys.
224 225 226 |
# File 'lib/active_cabinet/metaclass.rb', line 224 def to_h cabinet.to_h.map { |id, attributes| [id, new(attributes)] }.to_h end |
.where(query = nil) {|record| ... } ⇒ Array<Object>
Returns an array of records for which the block returns true. When query
is provided, it should be a Hash with a single key and value. The result will be records that have a matching attribute.
83 84 85 86 87 88 89 90 |
# File 'lib/active_cabinet/metaclass.rb', line 83 def where(query = nil) if query key, value = query.first all.select { |record| record[key] == value } else all.select { |record| yield record } end end |
Instance Method Details
#[](key) ⇒ Object
Returns the attribute value for the given key.
75 76 77 |
# File 'lib/active_cabinet.rb', line 75 def [](key) attributes[key] end |
#[]=(key, value) ⇒ Object
Sets the attribute value for the given key.
83 84 85 |
# File 'lib/active_cabinet.rb', line 83 def []=(key, value) attributes[key] = value end |
#allowed_attributes ⇒ Array<Symbol>
Returns an array containing required_attributes and optional_attributes.
25 26 27 |
# File 'lib/active_cabinet.rb', line 25 def allowed_attributes self.class.allowed_attributes end |
#default_attributes ⇒ Object
45 46 47 |
# File 'lib/active_cabinet.rb', line 45 def default_attributes self.class.default_attributes end |
#optional_attributes ⇒ Array<Symbol>
Returns an array of optional record attributes.
41 42 43 |
# File 'lib/active_cabinet.rb', line 41 def optional_attributes self.class.optional_attributes end |
#reload ⇒ self?
Reads the attributes of the record from the cabinet and returns the record itself. If the record is not stored on disk, returns nil
.
129 130 131 132 133 |
# File 'lib/active_cabinet.rb', line 129 def reload return nil unless saved? update cabinet[id] self end |
#required_attributes ⇒ Array<Symbol>
Returns an array of required record attributes.
33 34 35 |
# File 'lib/active_cabinet.rb', line 33 def required_attributes self.class.required_attributes end |
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
Returns true
when calling #respond_to?
with an attribute name.
117 118 119 120 121 |
# File 'lib/active_cabinet.rb', line 117 def respond_to_missing?(method_name, include_private = false) name = method_name name = name[0..-2].to_sym if name.to_s.end_with?('=', '?') attributes.has_key?(name) || super end |
#save ⇒ self, false
Saves the record to the cabinet if it is valid. Returns the record on success, or false
on failure.
139 140 141 142 143 144 145 146 |
# File 'lib/active_cabinet.rb', line 139 def save if valid? cabinet[id] = attributes self else false end end |
#saved? ⇒ Boolean
This method only verifies that the ID of the record exists. The attributes of the instance and the stored record may differ.
Returns true
if the record exists in the cabinet.
154 155 156 |
# File 'lib/active_cabinet.rb', line 154 def saved? cabinet.key? id end |
#to_h ⇒ Hash<Symbol, Object>
Returns a Hash of attributes
178 179 180 |
# File 'lib/active_cabinet.rb', line 178 def to_h attributes end |
#update(new_attributes) ⇒ Object
Update the record with new or modified attributes.
161 162 163 |
# File 'lib/active_cabinet.rb', line 161 def update(new_attributes) @attributes = attributes.merge(new_attributes.transform_keys &:to_sym) end |
#update!(new_attributes) ⇒ Object
Update the record with new or modified attributes, and save.
168 169 170 171 |
# File 'lib/active_cabinet.rb', line 168 def update!(new_attributes) update new_attributes save end |
#valid? ⇒ Boolean
Returns true
if the object is valid.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/active_cabinet.rb', line 52 def valid? missing_keys = required_attributes - attributes.keys if missing_keys.any? @error = "missing required attributes: #{missing_keys}" return false end if !optional_attributes or optional_attributes.any? invalid_keys = attributes.keys - allowed_attributes if invalid_keys.any? @error = "invalid attributes: #{invalid_keys}" return false end end true end |