accord 0.1.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.
Files changed (49) hide show
  1. data/.gitignore +7 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +7 -0
  4. data/Gemfile.lock +43 -0
  5. data/accord.gemspec +20 -0
  6. data/lib/accord.rb +4 -0
  7. data/lib/accord/adapter_registry.rb +106 -0
  8. data/lib/accord/base_registry.rb +73 -0
  9. data/lib/accord/declarations.rb +131 -0
  10. data/lib/accord/exceptions.rb +22 -0
  11. data/lib/accord/extendor.rb +36 -0
  12. data/lib/accord/extendor_container.rb +43 -0
  13. data/lib/accord/interface.rb +189 -0
  14. data/lib/accord/interface_body.rb +73 -0
  15. data/lib/accord/interface_members.rb +31 -0
  16. data/lib/accord/interface_method.rb +27 -0
  17. data/lib/accord/interfaces.rb +471 -0
  18. data/lib/accord/nested_key_hash.rb +66 -0
  19. data/lib/accord/ro.rb +65 -0
  20. data/lib/accord/signature_info.rb +76 -0
  21. data/lib/accord/specification.rb +83 -0
  22. data/lib/accord/subscription_registry.rb +76 -0
  23. data/lib/accord/tags.rb +25 -0
  24. data/lib/accord/version.rb +3 -0
  25. data/spec/adapter_registry_spec.rb +296 -0
  26. data/spec/declarations_spec.rb +144 -0
  27. data/spec/extendor_container_spec.rb +101 -0
  28. data/spec/extendor_spec.rb +203 -0
  29. data/spec/integration/adaptation_spec.rb +86 -0
  30. data/spec/integration/adapter_for_class_declaration_spec.rb +22 -0
  31. data/spec/integration/adapter_hook_spec.rb +41 -0
  32. data/spec/integration/default_adapters_spec.rb +81 -0
  33. data/spec/integration/hash_adapters_spec.rb +20 -0
  34. data/spec/integration/interface_declaration_spec.rb +258 -0
  35. data/spec/integration/multi_adapters_spec.rb +83 -0
  36. data/spec/integration/named_adapters_spec.rb +54 -0
  37. data/spec/integration/single_adapters_spec.rb +93 -0
  38. data/spec/integration/subscriptions_spec.rb +245 -0
  39. data/spec/integration/verification_spec.rb +215 -0
  40. data/spec/interface_body_spec.rb +157 -0
  41. data/spec/interface_members_spec.rb +57 -0
  42. data/spec/interface_spec.rb +147 -0
  43. data/spec/nested_key_hash_spec.rb +140 -0
  44. data/spec/signature_info_spec.rb +65 -0
  45. data/spec/spec_helper.rb +2 -0
  46. data/spec/specification_spec.rb +246 -0
  47. data/spec/subscription_registry_spec.rb +206 -0
  48. data/spec/tags_spec.rb +38 -0
  49. metadata +134 -0
@@ -0,0 +1,7 @@
1
+ bin/*
2
+ coverage/
3
+ pkg/*
4
+ .bundle
5
+ *.gem
6
+ *.swp
7
+ Session.vim
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'pry'
6
+ gem 'pry-doc'
7
+ gem 'simplecov', :require => false, :group => 'test'
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ accord (0.1.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ coderay (1.0.8)
10
+ diff-lcs (1.1.3)
11
+ method_source (0.8.1)
12
+ multi_json (1.6.1)
13
+ pry (0.9.12)
14
+ coderay (~> 1.0.5)
15
+ method_source (~> 0.8)
16
+ slop (~> 3.4)
17
+ pry-doc (0.4.4)
18
+ pry (>= 0.9.9.6)
19
+ yard (~> 0.8.1)
20
+ rspec (2.12.0)
21
+ rspec-core (~> 2.12.0)
22
+ rspec-expectations (~> 2.12.0)
23
+ rspec-mocks (~> 2.12.0)
24
+ rspec-core (2.12.2)
25
+ rspec-expectations (2.12.1)
26
+ diff-lcs (~> 1.1.3)
27
+ rspec-mocks (2.12.2)
28
+ simplecov (0.7.1)
29
+ multi_json (~> 1.0)
30
+ simplecov-html (~> 0.7.1)
31
+ simplecov-html (0.7.1)
32
+ slop (3.4.3)
33
+ yard (0.8.4.1)
34
+
35
+ PLATFORMS
36
+ ruby
37
+
38
+ DEPENDENCIES
39
+ accord!
40
+ pry
41
+ pry-doc
42
+ rspec
43
+ simplecov
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "accord/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "accord"
7
+ s.version = Accord::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["rcabralc"]
10
+ s.email = ["rcabralc@gmail.com"]
11
+ s.homepage = "http://rubygems.org/gems/accord"
12
+ s.summary = %q{Contracts and adaptation for Ruby}
13
+ s.description = %q{Contracts and adaptation for Ruby}
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_development_dependency('rspec')
20
+ end
@@ -0,0 +1,4 @@
1
+ require 'accord/exceptions'
2
+ require 'accord/adapter_registry'
3
+ require 'accord/subscription_registry'
4
+ require 'accord/interfaces'
@@ -0,0 +1,106 @@
1
+ require 'accord/interface'
2
+ require 'accord/base_registry'
3
+
4
+ module Accord
5
+ class AdapterLookup < BaseLookup
6
+ def first(required, provided, name='')
7
+ extendor = extendors.get(provided)
8
+ return unless extendor
9
+ return unless result = hash.detect_expansion(required) { |s| s.ancestors }
10
+ available = extendor.compact_map { |i| result[i] }
11
+ (available.detect { |h| h[name] } || {})[name]
12
+ end
13
+
14
+ def all(required, provided)
15
+ extendor = extendors.get(provided)
16
+ return {} unless extendor
17
+ hashes = hash.select_expansions(required + [provided]) do |key|
18
+ next key.ancestors.reverse if required.include?(key)
19
+ extendor.current.reverse
20
+ end
21
+ hashes.inject({}) { |result, h| result.merge(h) }
22
+ end
23
+ end
24
+
25
+ class AdapterRegistry < BaseRegistry
26
+ def self.lookup_class
27
+ AdapterLookup
28
+ end
29
+
30
+ def register(required, provided, name='', &value)
31
+ raise ArgumentError, "cannot register without a block" unless value
32
+ required = normalize_interfaces(required || [nil])
33
+ provided ||= Interface
34
+ registrations.by_order(required.size)[[required, provided, name]] = value
35
+ end
36
+
37
+ def unregister(required, provided, name, value=nil)
38
+ required = normalize_interfaces(required || [nil])
39
+ provided ||= Interface
40
+
41
+ lookup = registrations.by_order(required.size)
42
+ key = [required, provided, name]
43
+ old = lookup[key]
44
+
45
+ return if old.nil?
46
+ return if !value.nil? && !old.equal?(value)
47
+
48
+ lookup.delete(key)
49
+ end
50
+
51
+ def first(options={})
52
+ required = normalize_interfaces(options[:required] || [nil])
53
+ provided = options[:provided] || Interface
54
+ name = options[:name] || ''
55
+ registrations.by_order(required.size)[[required, provided, name]]
56
+ end
57
+
58
+ def all(options={})
59
+ required = normalize_interfaces(options[:required] || [nil])
60
+ provided = options[:provided] || Interface
61
+ registrations.by_order(required.size).partial(required, provided)
62
+ end
63
+
64
+ def lookup(required, provided, *args)
65
+ required = normalize_interfaces(required || [nil])
66
+ provided ||= Interface
67
+ options = args.last.is_a?(Hash) ? args.pop : {}
68
+ name = args.last || ''
69
+ default = options.delete(:default)
70
+ lookup = registrations.by_order(required.size)
71
+ lookup.first(required, provided, name) || default
72
+ end
73
+
74
+ def lookup_all(required, provided)
75
+ required = normalize_interfaces(required || [nil])
76
+ provided ||= Interface
77
+ lookup = registrations.by_order(required.size)
78
+ lookup.all(required, provided)
79
+ end
80
+
81
+ def get(objects, provided, *args)
82
+ options = args.last.is_a?(Hash) ? args.pop : {}
83
+ name = args.last || ''
84
+ default = options.delete(:default)
85
+ factory = lookup(map_provided_by(objects), provided, name, options)
86
+ return (factory.call(*objects) || default) if factory
87
+ default
88
+ end
89
+ end
90
+
91
+ class << self
92
+ def install_default_adapter_hook
93
+ install_adapter_hook(Proc.new { |provided, *objects|
94
+ default_adapter_registry.get(objects, provided)
95
+ })
96
+ end
97
+
98
+ def clear_default_adapter_hook
99
+ @default_adapter_registry = nil
100
+ end
101
+
102
+ def default_adapter_registry
103
+ @default_adapter_registry ||= AdapterRegistry.new
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,73 @@
1
+ require 'accord/declarations'
2
+ require 'accord/extendor_container'
3
+ require 'accord/nested_key_hash'
4
+
5
+ module Accord
6
+ class Registrations
7
+ def initialize(lookup_class)
8
+ @lookup_class = lookup_class
9
+ end
10
+
11
+ def by_order order
12
+ hash[order] ||= @lookup_class.new(order, extendors)
13
+ end
14
+
15
+ private
16
+
17
+ def hash
18
+ @hash ||= {}
19
+ end
20
+
21
+ def extendors
22
+ @extendors ||= ExtendorContainer.new
23
+ end
24
+ end
25
+
26
+ class BaseLookup
27
+ attr_reader :order, :extendors, :hash
28
+
29
+ def initialize(order, extendors)
30
+ @order = order
31
+ @extendors = extendors
32
+ @hash = NestedKeyHash.new
33
+ super()
34
+ end
35
+
36
+ def [](key)
37
+ required, provided, name = key
38
+ hash[required + [provided] + [name]]
39
+ end
40
+
41
+ def []=(key, value)
42
+ required, provided, name = key
43
+ extendors.add(provided)
44
+ hash[required + [provided] + [name]] = value
45
+ end
46
+
47
+ def partial(required, provided)
48
+ (hash[required + [provided]] || []).sort_by { |name, value| name }
49
+ end
50
+
51
+ def delete(key)
52
+ required, provided, name = key
53
+ extendors.delete(provided)
54
+ hash.delete(required + [provided] + [name])
55
+ end
56
+ end
57
+
58
+ class BaseRegistry
59
+ def map_provided_by(objects)
60
+ objects.map { |object| Accord::Declarations.provided_by(object) }
61
+ end
62
+
63
+ private
64
+
65
+ def registrations
66
+ @registrations ||= Registrations.new(self.class.lookup_class)
67
+ end
68
+
69
+ def normalize_interfaces(interfaces)
70
+ interfaces.map { |i| i.nil?? Accord::Interface : i }
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,131 @@
1
+ require 'accord/specification'
2
+
3
+ module Accord
4
+ module Declarations
5
+ class Declaration < Specification
6
+ def extends?(interface)
7
+ super(interface) && interfaces.include?(interface)
8
+ end
9
+
10
+ def + other
11
+ Declaration.new(interfaces + other.interfaces)
12
+ end
13
+
14
+ def - other
15
+ Declaration.new(interfaces.select { |i|
16
+ !other.interfaces.any? { |j| i.extends?(j) }
17
+ })
18
+ end
19
+ end
20
+
21
+ class Implements < Declaration
22
+ attr_reader :name
23
+
24
+ def initialize(inherit)
25
+ @inherit = inherit
26
+ set_name(inherit)
27
+ @declared = []
28
+ super(bases_from_inherit)
29
+ end
30
+
31
+ def declare(*interfaces)
32
+ @declared.concat(normalized(interfaces)).uniq!
33
+ new_bases = @declared.dup
34
+ bases_from_inherit.each do |base|
35
+ new_bases << base if !@declared.include?(base)
36
+ end
37
+ self.bases = new_bases
38
+ end
39
+
40
+ def declare_only(*interfaces)
41
+ @declared = []
42
+ self.inherit = nil
43
+ declare(*interfaces)
44
+ end
45
+
46
+ def inspect
47
+ "#<Implemented by #{name}>"
48
+ end
49
+
50
+ private
51
+
52
+ attr_accessor :inherit
53
+
54
+ def set_name(inherit)
55
+ if inherit.respond_to?(:name)
56
+ if inherit.name.to_s != ''
57
+ @name = inherit.name
58
+ else
59
+ @name = inherit.inspect
60
+ end
61
+ else
62
+ @name = inherit.inspect
63
+ end
64
+ end
65
+
66
+ def bases_from_inherit
67
+ if inherit.is_a?(Module)
68
+ ancestors = inherit.ancestors
69
+ ancestors.delete(inherit)
70
+ ancestors.map { |ancestor| Declarations.implemented_by(ancestor) }
71
+ else
72
+ []
73
+ end
74
+ end
75
+
76
+ def normalized(args)
77
+ enum_for(:normalize, args).to_a
78
+ end
79
+
80
+ def normalize(args, &block)
81
+ if args.is_a?(InterfaceClass) || args.is_a?(Implements)
82
+ block.call(args)
83
+ else
84
+ args.each do |arg|
85
+ normalize(arg, &block)
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ class << self
92
+ def provided_by(object)
93
+ implemented_by(object.class) + directly_provided_by(object)
94
+ end
95
+
96
+ def directly_provided_by(object)
97
+ object.instance_eval do
98
+ @_accord_provides_ ||= Declaration.new
99
+ end
100
+ end
101
+
102
+ def implemented_by(factory)
103
+ factory.instance_eval do
104
+ @_accord_implements_ ||= Implements.new(self)
105
+ end
106
+ end
107
+
108
+ def implements(cls, *interfaces)
109
+ implemented_by(cls).declare(*interfaces)
110
+ end
111
+
112
+ def implements_only(cls, *interfaces)
113
+ implemented_by(cls).declare_only(*interfaces)
114
+ end
115
+
116
+ def directly_provides(object, *interfaces)
117
+ object.instance_eval do
118
+ @_accord_provides_ = Declaration.new(interfaces)
119
+ end
120
+ end
121
+
122
+ def also_provides(object, *interfaces)
123
+ directly_provides(object, directly_provided_by(object), *interfaces)
124
+ end
125
+
126
+ def no_longer_provides(object, interface)
127
+ directly_provides(object, directly_provided_by(object) - interface)
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,22 @@
1
+ module Accord
2
+ class Error < StandardError; end
3
+
4
+ class Invalid < Error; end
5
+
6
+ class DoesNotImplement < Invalid
7
+ def initialize(interface)
8
+ @interface = interface
9
+ end
10
+ end
11
+
12
+ class BrokenImplementation < Invalid
13
+ def initialize(interface, method_name)
14
+ @interface = interface
15
+ @method_name = method_name
16
+ end
17
+
18
+ def to_s
19
+ "signature mismatch for #{@interface.inspect}##{@method_name}"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ module Accord
2
+ class Extendor
3
+ def initialize
4
+ @current = []
5
+ end
6
+
7
+ def add(new)
8
+ return if @current.include?(new)
9
+ @current = (
10
+ @current.select { |i| new.extends?(i) } +
11
+ [new] +
12
+ @current.select { |i| !new.extends?(i) }
13
+ )
14
+ end
15
+
16
+ def delete(old)
17
+ @current.delete_if { |i| i.equal?(old) }
18
+ end
19
+
20
+ def empty?
21
+ @current.empty?
22
+ end
23
+
24
+ def current
25
+ @current.dup
26
+ end
27
+
28
+ def compact_map
29
+ @current.map { |i| yield(i) }.compact
30
+ end
31
+
32
+ def flat_map
33
+ @current.flat_map { |i| yield(i) }
34
+ end
35
+ end
36
+ end