This is a sampling of code meant to demonstrate collections for attrio. #5
Conversation
This code is not tested yet. Readme changes are examples of using Collections. attributes_parser had collection, create_collections, and fetch_container_type methods added. collection is a class parallel to Attribute for use with collections.
Hello,
|
…ute. I used Attrio::AttributeBase as the module name for the shared methods, but I'm not happy with that name and when I come up with a better name, I'll rename it. I left the writer methods in Attrio::Attribute since only Attribute is going to use those methods. Factoring those into another module is something to keep in mind.
I used the DelgateClass as the basis for inheritence because it offers an easy way to wrap and extend classes while delgating all other calls to the original class. Specs are very simple, just testing creation and that they are acting a little like their delgate.
…ble modules moved attr_reader into Readable module. removed InstanceMethods module from Readable decided not to define initialize in a module. This could be readdressed. If so, then the common initialize method needs to call a class specific method for additional initialization. moved Collection autoload block to the bottom of Attrio
fixed a bug in hash#initialize
Earlier I was working thinking that Array.Hash.Set were being used as a parallel to Attribute, when actually those should be used as the instance values of a collection, and that we needed Collection as a class to be parallel to Attribute. With that new understanding, I changed Collectable back to Collection, and have been building that class up to do it's job. Followed the lead of Attribute and so have no specs yet, though there isn't anything very complicated in that class. Hash is the first of the collections that I've started to flesh out. the type_cast method shoul be factored out into a Collections::Base class which we can also use to set the interface with the always popular NotImplementedError. The key_method works well for providing a unique value to use for the key of the hash. There seems to be a use case for some functionality around attempting to add_elements. Both in raising errors when a key is used again (redefining a value) and when a value can't be coerced into the correct type. If i wanted to add the ability to ignore values that can't be cast, I'd put in a ignore_type_error option check and add in a catch clause to catch TypeErrors then continue instead of reraiseing. If I wanted to add the ability to raise errors when reusing a key, I'd add a raise_on_duplicate option, and put in a check on the key value before the new value is assigned. There are a few gotcha's with the way DelegateClass works, and so I have kind_of? overridden, but I'm not sure that's needed. I'm sure when I implement Array and Set it'll become clear what needs to be done. While all specs are passing, this commit is not complete by itself.
There is the potential for adding more advanced find methods. Don't think they are needed, but they are there. for a collection the default value is going to be the return value of find_element when no element was actually found. Users can use this to provide useful defaults such as an appropriate Null class. Should we provide an :initial_values options to populate the initial collection? Next up is to finish the CollectionBuilder and Collection and then to test integration
Changed Collections to use SimpleDelegator instead of ClassDelegator updated specs to use .create_collection
Alright, things are getting close now. Have a few questions though.
I still need to add integration tests to test the whole thing top to bottom. |
Ok after some more code reading and thought, this is the next set of stuff that I'm going to implement. :collections_as options
In addition, I'm going to differ from the attributes and make the collections hash a first class object, move most of the functionality for reset and initialize into the object and use dispatch to connect the instances. If it works out, I'd say that the attributes hash could be similarly refactored. This separation offers some nice opportunities. They could be used as the basis of a plugin system which allows the possibility to add depth in the way classes are created, marshalled, stored, initiliaze... :unfound_value option
:initial_value option
|
This code is not tested yet. Readme changes are examples of using Collections. attributes_parser had collection, create_collections, and fetch_container_type methods added. collection is a class parallel to Attribute for use with collections.
…ute. I used Attrio::AttributeBase as the module name for the shared methods, but I'm not happy with that name and when I come up with a better name, I'll rename it. I left the writer methods in Attrio::Attribute since only Attribute is going to use those methods. Factoring those into another module is something to keep in mind.
I used the DelgateClass as the basis for inheritence because it offers an easy way to wrap and extend classes while delgating all other calls to the original class. Specs are very simple, just testing creation and that they are acting a little like their delgate.
…ble modules moved attr_reader into Readable module. removed InstanceMethods module from Readable decided not to define initialize in a module. This could be readdressed. If so, then the common initialize method needs to call a class specific method for additional initialization. moved Collection autoload block to the bottom of Attrio
fixed a bug in hash#initialize
Earlier I was working thinking that Array.Hash.Set were being used as a parallel to Attribute, when actually those should be used as the instance values of a collection, and that we needed Collection as a class to be parallel to Attribute. With that new understanding, I changed Collectable back to Collection, and have been building that class up to do it's job. Followed the lead of Attribute and so have no specs yet, though there isn't anything very complicated in that class. Hash is the first of the collections that I've started to flesh out. the type_cast method shoul be factored out into a Collections::Base class which we can also use to set the interface with the always popular NotImplementedError. The key_method works well for providing a unique value to use for the key of the hash. There seems to be a use case for some functionality around attempting to add_elements. Both in raising errors when a key is used again (redefining a value) and when a value can't be coerced into the correct type. If i wanted to add the ability to ignore values that can't be cast, I'd put in a ignore_type_error option check and add in a catch clause to catch TypeErrors then continue instead of reraiseing. If I wanted to add the ability to raise errors when reusing a key, I'd add a raise_on_duplicate option, and put in a check on the key value before the new value is assigned. There are a few gotcha's with the way DelegateClass works, and so I have kind_of? overridden, but I'm not sure that's needed. I'm sure when I implement Array and Set it'll become clear what needs to be done. While all specs are passing, this commit is not complete by itself.
There is the potential for adding more advanced find methods. Don't think they are needed, but they are there. for a collection the default value is going to be the return value of find_element when no element was actually found. Users can use this to provide useful defaults such as an appropriate Null class. Should we provide an :initial_values options to populate the initial collection? Next up is to finish the CollectionBuilder and Collection and then to test integration
Changed Collections to use SimpleDelegator instead of ClassDelegator updated specs to use .create_collection
…collection_proposal
We prefer :c_as, but will use :collections_as if present attrio.rb changed to include collection hash and methods added AttributesParser#add_collection refactored collection to use #add_collection Extended Reset to have a #define_attrio_collection_reset method that defines :reset_collections, :initialize_collections, :empty_collections all of which call a method on each collection Extended CollectionBuilder to delegate to reset_collection, empty_collection, and initialize_collection on the object. Refactored calls into a set_delegate and define_delegate methods Extended Collection to have methods for _name and _visibility methods for reset, empty and initialize collections. This is ripe for a refactor, maybe setting up method_missing and crunching the bits into a Constant hash using the keys as the base and a string format as the value Refactored Array, Set, and Hash to use __mk_empty_collection__ in initialize and empty_collection methods Refactored the type check into #__normalize_value__ method that handles calls for default values and type_cast everything else. Extended Inspect to include collections replaced all mocks with doubles replaced all stubs with doubles cleaned up a few TODOs
fixed type in set_spec altered common to return self from #reset_collection, empty_collection, and #initialize_collection
added values.flatten! to all #add_element(values)
Ok, This now contains a working implementation of collections with working and passing specs. There are some small changes to be made, mostly to match coding styles (I tend to use the I invite all comments, suggestions, changes.... Let me know what you think. thanks! |
So... |
This request is meant for discussion, it is unready for merging.
I'm going to do this work, but I wanted to know what you guys thought of it. It could also be implemented as a plugin for Attrio.
I love the Attrio code so far, and I'm planning to use it in another project soon. However, I need to have similar functionality for collections of objects in addition to the attributes already provided. This outlines one way to accomplish this.
We start by modifying AttributesParser to include a #collection public method which allow you to define collections. Each collection has 3 groups of methods. a Reader which gives you full access to the collection. a AddElement, which adds a new element to the array, and a FindElement which will allow you to read a single element of the collection based on an index.
Collections may be 1 of 3 types. With no special options, Attrio::Collection will create an Array. With a :unique options key set to true and no :index key, Attrio::Collection will create a Set. With a :unique key set to true and an :index key, Attrio::Collection will create a hash, and will call the method defined by the :index key and use the result as the hash key. This would allow you to do something like...
the students collection would be a hash in this case, using the return value from student.student_id, as the key.
The schedule collection would be a set, and the reading_materials collection would be an array.
FindElement can be used for any container, the default value defined for the collection will be returned if no element is found.
AddElement should be structured to accept an array of hashes that can be used to add a number of elements at the same time as well as adding only a single one at a time.
This code is not tested yet.
Readme changes are examples of using Collections.
attributes_parser had collection, create_collections, and
fetch_container_type methods added.
collection is a class parallel to Attribute for use with collections.