accord 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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