behaves 0.2.0 → 0.3.0
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +29 -0
- data/lib/behaves.rb +61 -11
- data/lib/behaves/version.rb +1 -1
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 4c594bfaa286e772150c5e2c31c24fe2b4684cd4fbd68f16341cc30b6c9ebfe0
         | 
| 4 | 
            +
              data.tar.gz: 5aa8758aee6a60747332ac0515a73a1bc3a63d3336870237b13290b09f1cda39
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 87e3bf7f5c4c218c7e2cd8de1540577455cb51340bfc51f28fc6043a025315af8d07fe8802cdd750488152eb9649b219147534cc17ea08682c7b8b410312e476
         | 
| 7 | 
            +
              data.tar.gz: 004aa2b59f31bd916b4df9380ef8d9a5bb3f6a24d88a09c549a85fbfd5b5a6cfc74d379d6c24681e19e6461aac9856be72253358ef66690da4e625f183b058c6
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -144,6 +144,35 @@ This extends to more than just method implementation too, you can do anything yo | |
| 144 144 |  | 
| 145 145 | 
             
            *Do note that if you use this extensively, you might be better off using inheritance, since this will create more `Method` objects than inheritance.*
         | 
| 146 146 |  | 
| 147 | 
            +
            ### Private Behaviors
         | 
| 148 | 
            +
             | 
| 149 | 
            +
            Private behaviors can be defined like so:
         | 
| 150 | 
            +
             | 
| 151 | 
            +
            ```ruby
         | 
| 152 | 
            +
            class Interface
         | 
| 153 | 
            +
              extend Behaves
         | 
| 154 | 
            +
             | 
| 155 | 
            +
              implements :foo
         | 
| 156 | 
            +
              implements :bar, private: true
         | 
| 157 | 
            +
            end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
            class Implementor
         | 
| 160 | 
            +
              extend Behaves
         | 
| 161 | 
            +
             | 
| 162 | 
            +
              behaves_like Interface
         | 
| 163 | 
            +
             | 
| 164 | 
            +
              def foo
         | 
| 165 | 
            +
                123
         | 
| 166 | 
            +
              end
         | 
| 167 | 
            +
             | 
| 168 | 
            +
              private
         | 
| 169 | 
            +
             | 
| 170 | 
            +
              def bar
         | 
| 171 | 
            +
                456
         | 
| 172 | 
            +
              end
         | 
| 173 | 
            +
            end
         | 
| 174 | 
            +
            ```
         | 
| 175 | 
            +
             | 
| 147 176 | 
             
            ## Tips
         | 
| 148 177 | 
             
            If you do not want to type `extend Behaves` every time, you can monkey patch `Behaves` onto `Object` class, like so:
         | 
| 149 178 |  | 
    
        data/lib/behaves.rb
    CHANGED
    
    | @@ -2,8 +2,15 @@ require 'behaves/version' | |
| 2 2 | 
             
            require 'set'
         | 
| 3 3 |  | 
| 4 4 | 
             
            module Behaves
         | 
| 5 | 
            -
              def implements(*methods)
         | 
| 6 | 
            -
                @ | 
| 5 | 
            +
              def implements(*methods, **opts)
         | 
| 6 | 
            +
                @_public_behaviors  ||= Set.new
         | 
| 7 | 
            +
                @_private_behaviors ||= Set.new
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                if opts[:private] == true
         | 
| 10 | 
            +
                  @_private_behaviors += Set.new(methods)
         | 
| 11 | 
            +
                else
         | 
| 12 | 
            +
                  @_public_behaviors  += Set.new(methods)
         | 
| 13 | 
            +
                end
         | 
| 7 14 | 
             
              end
         | 
| 8 15 |  | 
| 9 16 | 
             
              def inject_behaviors (&block)
         | 
| @@ -12,25 +19,68 @@ module Behaves | |
| 12 19 |  | 
| 13 20 | 
             
              def behaves_like(klass)
         | 
| 14 21 | 
             
                add_injected_behaviors(klass)
         | 
| 15 | 
            -
                at_exit { | 
| 22 | 
            +
                at_exit {
         | 
| 23 | 
            +
                  check_for_unimplemented(klass, :public)
         | 
| 24 | 
            +
                  check_for_unimplemented(klass, :private)
         | 
| 25 | 
            +
                }
         | 
| 16 26 | 
             
              end
         | 
| 17 27 |  | 
| 18 28 | 
             
              private
         | 
| 19 29 |  | 
| 20 | 
            -
              def check_for_unimplemented(klass)
         | 
| 21 | 
            -
                required = defined_behaviors(klass)
         | 
| 30 | 
            +
              def check_for_unimplemented(klass, scope)
         | 
| 31 | 
            +
                required = defined_behaviors(klass, scope)
         | 
| 22 32 |  | 
| 23 | 
            -
                 | 
| 24 | 
            -
             | 
| 25 | 
            -
                unimplemented = required - implemented
         | 
| 33 | 
            +
                unimplemented = required - implemented(scope)
         | 
| 26 34 |  | 
| 27 35 | 
             
                return if unimplemented.empty?
         | 
| 28 36 |  | 
| 29 | 
            -
             | 
| 37 | 
            +
             | 
| 38 | 
            +
                # basic "unimplemented method" error message
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                err = <<~ERR
         | 
| 41 | 
            +
                  \n\n  Expected `#{self}` to behave like `#{klass}`, but the following #{scope} methods are unimplemented:\n
         | 
| 42 | 
            +
                  #{unimplemented.to_a.map{|method| "    * `#{method}`"}.join("\n")}\n
         | 
| 43 | 
            +
                ERR
         | 
| 44 | 
            +
             | 
| 45 | 
            +
             | 
| 46 | 
            +
                # add "wrong scope" message
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                other_scopes            = (scope == :public) ? [:private] : [:public]
         | 
| 49 | 
            +
                methods_in_other_scopes = Set.new( other_scopes.map{|s| implemented(s)}.map(&:to_a).flatten.compact )
         | 
| 50 | 
            +
                methods_in_wrong_scope  = unimplemented && methods_in_other_scopes
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                if !methods_in_wrong_scope.empty?
         | 
| 53 | 
            +
                  err += <<~ERR
         | 
| 54 | 
            +
                    \n  The following methods appear to be defined, but in the wrong scope:\n
         | 
| 55 | 
            +
                    #{methods_in_wrong_scope.to_a.map{|method| "    * `#{method}`"}.join("\n")}\n\n
         | 
| 56 | 
            +
                  ERR
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
             | 
| 60 | 
            +
                raise NotImplementedError, err
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              def implemented(scope)
         | 
| 64 | 
            +
                lookups = {
         | 
| 65 | 
            +
                  public:           :instance_methods,
         | 
| 66 | 
            +
                  private:  :private_instance_methods,
         | 
| 67 | 
            +
                }
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                method_lookup_sym = lookups.fetch(scope) do
         | 
| 70 | 
            +
                  raise ArgumentError.new <<~ERR
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    Invalid `scope`: #{scope}
         | 
| 73 | 
            +
                    Valid `scope`s include: #{lookups.keys.map{|sym| "`#{sym.inspect}`"}.join(', ')}
         | 
| 74 | 
            +
                  ERR
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                methods = self.send(method_lookup_sym, false)
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                Set.new(methods)
         | 
| 30 80 | 
             
              end
         | 
| 31 81 |  | 
| 32 | 
            -
              def defined_behaviors(klass)
         | 
| 33 | 
            -
                if behaviors = klass.instance_variable_get("@ | 
| 82 | 
            +
              def defined_behaviors(klass, scope)
         | 
| 83 | 
            +
                if behaviors = klass.instance_variable_get(:"@_#{scope}_behaviors")
         | 
| 34 84 | 
             
                  behaviors
         | 
| 35 85 | 
             
                else
         | 
| 36 86 | 
             
                  raise NotImplementedError, "Expected `#{klass}` to define behaviors, but none found."
         | 
    
        data/lib/behaves/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: behaves
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.3.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Edison Yap
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2019-06- | 
| 11 | 
            +
            date: 2019-06-20 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: pry
         |