Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds basic functionality for managing attributes with several options #121

Merged
merged 8 commits into from
Feb 26, 2018

Conversation

d-Pixie
Copy link
Member

@d-Pixie d-Pixie commented Oct 18, 2017

Right now we do .with( read_only: true ) to add options to an attribute declaration. This PR allows you to use .is( :read_only ) instead. It also works with several options, so .with( read_only: true, required: true, sortable: true, filterable: true ) becomes .is( :read_only, :required, :sortable, :filterable ) instead.

It also adds an .attributes() method to our instances where you get all the attributes on the class by default but can give it one or more options to filter the list, like

customer = Fortnox::API::Model::Customer.new( name: 'Fooling Around')
=> #<Fortnox::API::Model::Customer url=nil address1=nil address2=nil city=nil country=nil comments=nil currency=nil cost_center=nil country_code=nil customer_number=nil default_delivery_types=nil default_templates=nil delivery_address1=nil delivery_address2=nil delivery_city=nil delivery_country=nil delivery_country_code=nil delivery_fax=nil delivery_name=nil delivery_phone1=nil delivery_phone2=nil delivery_zip_code=nil email=nil email_invoice=nil email_invoice_bcc=nil email_invoice_cc=nil email_offer=nil email_offer_bcc=nil email_offer_cc=nil email_order=nil email_order_bcc=nil email_order_cc=nil fax=nil invoice_administration_fee=nil invoice_discount=nil invoice_freight=nil invoice_remark=nil name="Fooling Around" organisation_number=nil our_reference=nil phone1=nil phone2=nil price_list=nil project=nil sales_account=nil show_price_vat_included=nil terms_of_delivery=nil terms_of_payment=nil type=nil vat_number=nil vat_type=nil visiting_address=nil visiting_city=nil visiting_country=nil visiting_country_code=nil visiting_zip_code=nil way_of_delivery=nil your_reference=nil zip_code=nil>

customer.attributes( :read_only )
=> [[:url,
  #<Dry::Types::Constructor type=#<Dry::Types::Sum::Constrained left=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=NilClass options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>> right=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=String options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[String]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[String]}>> options={}>>],
 [:country,
  #<Dry::Types::Constructor type=#<Dry::Types::Sum::Constrained left=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=NilClass options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>> right=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=String options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[String]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[String]}>> options={}>>],
 [:delivery_country,
  #<Dry::Types::Constructor type=#<Dry::Types::Sum::Constrained left=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=NilClass options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>> right=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=String options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[String]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[String]}>> options={}>>],
 [:visiting_country,
  #<Dry::Types::Constructor type=#<Dry::Types::Sum::Constrained left=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=NilClass options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>> right=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=String options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[String]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[String]}>> options={}>>]]

Comments on implementation and strategy for this is welcome, it's just a rough WiP.

When we have this we can easily get a list of sortable and filterable attributes into our models and use that info when the filtering methods are called to verify that only attributes that are actually filter attributes are sent to Fortnox.

@felixyz
Copy link
Contributor

felixyz commented Oct 18, 2017

I think the API looks cleaner, and especially the possibility to filter seems great.

Would you say with should be deprecated in favor of this? If possible I think it's probably better to do a complete switch to the new style.

@d-Pixie
Copy link
Member Author

d-Pixie commented Oct 18, 2017

with is part of dry-types, so we can't remove it. But I think that we should only use is when the feature is done. I'll include that refactoring I the final PR.

@d-Pixie d-Pixie mentioned this pull request Oct 19, 2017
@d-Pixie d-Pixie changed the title Adds basic functionality for managing attributes with several options [WiP] Adds basic functionality for managing attributes with several options Feb 19, 2018
Copy link
Member

@ehannes ehannes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good :)

@@ -30,6 +30,17 @@ def self.new( hash )
IceNine.deep_freeze( obj )
end

# This filtering logic could be improved since it is currently O(N*M).
def attributes( *options )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add tests for this this method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yepp.

def attributes( *options )
return self.class.schema if options.nil?

options = Array(options)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe choose a different variable name here instead of mutating options?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, but on the other hand I'm just ensuring that the argument is an array, I'm not really mutating it. Also I think this is unnecessary, the argument should already be an array from how I get them. I'll verify.

@@ -27,7 +27,7 @@ class Customer < Fortnox::API::Model::Base
# )

#Url Direct URL to the record
attribute :url, Types::Nullable::String.with( read_only: true )
attribute :url, Types::Nullable::String.is( :read_only )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -1,6 +1,25 @@
require 'dry-struct'
require 'dry-types'

module Dry
module Types
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely add test this override. We want to verify it's working as intended and we also want to see if it breaks when we update Dry::Types in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make this a refinement instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great suggestion! Then we only need to test that our Types is behaving as intended and it will prevent there modifications from leaking too :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out you can't refine modules, only classes. I tried a few workarounds but didn't get it to work at all with refinements :/
I'll probably open a PR against dry types and try to get this into the real repo. If not I'll ask @solnic to advice on how to do this refinement in a safe way ...

@d-Pixie d-Pixie changed the title [WiP] Adds basic functionality for managing attributes with several options Adds basic functionality for managing attributes with several options Feb 26, 2018
@d-Pixie d-Pixie merged commit 80ee4b2 into development Feb 26, 2018
@d-Pixie d-Pixie deleted the better_attribute_options branch February 26, 2018 13:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants