mithril 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/CHANGELOG.md +31 -0
  2. data/README.md +0 -0
  3. data/bin/mithril +5 -0
  4. data/lib/mithril.rb +13 -0
  5. data/lib/mithril/controllers.rb +7 -0
  6. data/lib/mithril/controllers/abstract_controller.rb +130 -0
  7. data/lib/mithril/controllers/mixins.rb +7 -0
  8. data/lib/mithril/controllers/mixins/actions_base.rb +114 -0
  9. data/lib/mithril/controllers/mixins/help_actions.rb +46 -0
  10. data/lib/mithril/controllers/mixins/mixin_with_actions.rb +27 -0
  11. data/lib/mithril/controllers/proxy_controller.rb +89 -0
  12. data/lib/mithril/mixin.rb +33 -0
  13. data/lib/mithril/parsers.rb +7 -0
  14. data/lib/mithril/parsers/simple_parser.rb +57 -0
  15. data/lib/mithril/request.rb +11 -0
  16. data/lib/mithril/version.rb +5 -0
  17. data/spec/matchers/be_kind_of_spec.rb +50 -0
  18. data/spec/matchers/construct_spec.rb +49 -0
  19. data/spec/matchers/respond_to_spec.rb +158 -0
  20. data/spec/mithril/controllers/_text_controller_helper.rb +81 -0
  21. data/spec/mithril/controllers/abstract_controller_helper.rb +118 -0
  22. data/spec/mithril/controllers/abstract_controller_spec.rb +15 -0
  23. data/spec/mithril/controllers/mixins/actions_base_helper.rb +121 -0
  24. data/spec/mithril/controllers/mixins/actions_base_spec.rb +18 -0
  25. data/spec/mithril/controllers/mixins/help_actions_helper.rb +111 -0
  26. data/spec/mithril/controllers/mixins/help_actions_spec.rb +19 -0
  27. data/spec/mithril/controllers/mixins/mixin_with_actions_spec.rb +44 -0
  28. data/spec/mithril/controllers/proxy_controller_helper.rb +111 -0
  29. data/spec/mithril/controllers/proxy_controller_spec.rb +14 -0
  30. data/spec/mithril/mixin_helper.rb +54 -0
  31. data/spec/mithril/mixin_spec.rb +17 -0
  32. data/spec/mithril/parsers/simple_parser_spec.rb +85 -0
  33. data/spec/mithril/request_spec.rb +72 -0
  34. data/spec/mithril_spec.rb +25 -0
  35. data/spec/spec_helper.rb +15 -0
  36. data/spec/support/factories/action_factory.rb +7 -0
  37. data/spec/support/factories/request_factory.rb +11 -0
  38. data/spec/support/matchers/be_kind_of.rb +23 -0
  39. data/spec/support/matchers/construct.rb +49 -0
  40. data/spec/support/matchers/respond_to.rb +52 -0
  41. metadata +142 -0
@@ -0,0 +1,33 @@
1
+ # lib/mithril/mixin.rb
2
+
3
+ module Mithril
4
+ # Implements module-based inheritance of both class- and instance-level
5
+ # methods.
6
+ module Mixin
7
+ def mixins
8
+ @mixins ||= []
9
+ end # accessor mixins
10
+
11
+ def mixins=(ary)
12
+ @mixins = ary
13
+ end # mutator mixins
14
+
15
+ private
16
+ # Alternative to Module.extend that also provides inheritance of class-level
17
+ # methods defined through an (optional) ClassMethods module.
18
+ def mixin(source_module) # :doc:
19
+ include source_module
20
+
21
+ return unless source_module.respond_to? :mixins
22
+
23
+ self.mixins = source_module.mixins.dup || []
24
+ self.mixins << source_module
25
+
26
+ self.mixins.each do |mixin|
27
+ if mixin.const_defined? :ClassMethods
28
+ extend mixin::ClassMethods
29
+ end # if
30
+ end # each
31
+ end # method mixin
32
+ end # module Mixin
33
+ end # module
@@ -0,0 +1,7 @@
1
+ # lib/mithril/parsers.rb
2
+
3
+ require 'mithril'
4
+
5
+ module Mithril
6
+ module Parsers; end
7
+ end # module
@@ -0,0 +1,57 @@
1
+ # lib/mithril/parsers/simple_parser.rb
2
+
3
+ require 'mithril/parsers'
4
+
5
+ module Mithril::Parsers
6
+ class SimpleParser
7
+ def initialize(actions)
8
+ @actions = actions
9
+ end # method initialize
10
+
11
+ # Takes a string input and separates into words, then identifies a matching
12
+ # action (if any) and remaining arguments. Returns both the command and the
13
+ # arguments array, so usage can be as follows:
14
+ #
15
+ # @example With a "say" command:
16
+ # command, args = parse_command("say Greetings programs!")
17
+ # #=> command = :say, args = ["greetings", "programs"]
18
+ # @example With no "hello" command:
19
+ # command, args = parse_command("Hello world")
20
+ # #=> command = nil, args = ["hello", "world"]
21
+ #
22
+ # @param [String] text Expects a string composed of one or more words,
23
+ # separated by whitespace or hyphens.
24
+ # @return [Array] A two-element array consisting of the command and an
25
+ # array of the remaining text arguments (if any), or [nil, args] if no
26
+ # matching action was found.
27
+ def parse_command(text)
28
+ words = wordify preprocess_input text
29
+
30
+ key = nil
31
+ args = []
32
+
33
+ while 0 < words.count
34
+ key = words.join('_').intern
35
+
36
+ return key, args if @actions.has_action? key
37
+
38
+ args.unshift words.pop
39
+ end # while
40
+
41
+ return nil, args
42
+ end # method parse_command
43
+
44
+ private
45
+ # @!visibility public
46
+ def preprocess_input(text)
47
+ text.downcase.
48
+ gsub(/[\"?!\-',.:\(\)\[\]\;]/, ' ').
49
+ gsub(/(\s+)/, ' ').strip
50
+ end # method preprocess_input
51
+
52
+ # @!visibility public
53
+ def wordify(text)
54
+ text.split(/\s+/)
55
+ end # method wordify
56
+ end # class
57
+ end # module
@@ -0,0 +1,11 @@
1
+ # lib/mithril/request.rb
2
+
3
+ module Mithril
4
+ class Request
5
+ def initialize(session = {})
6
+ @session = session
7
+ end # constructor
8
+
9
+ attr_accessor :session, :input, :output
10
+ end # class Request
11
+ end # module
@@ -0,0 +1,5 @@
1
+ # lib/mithril/version.rb
2
+
3
+ module Mithril
4
+ VERSION = '0.2.0'
5
+ end # module Mithril
@@ -0,0 +1,50 @@
1
+ # spec/matchers/be_kind_of_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe RSpec::Matchers::BuiltIn::BeAKindOf do
6
+ let :custom_module do Module.new; end
7
+ let :custom_class do Class.new.tap { |c| c.send :include, custom_module }; end
8
+ let :custom_subclass do Class.new custom_class; end
9
+
10
+ let :string do "string"; end
11
+ let :module_instance do Object.new.extend custom_module; end
12
+ let :class_instance do custom_class.new; end
13
+ let :subclass_instance do custom_subclass.new; end
14
+
15
+ describe "with type" do
16
+ specify { expect(nil).to be_a NilClass }
17
+ specify { expect(nil).not_to be_a String }
18
+
19
+ specify { expect(string).to be_a String }
20
+ specify { expect(string).not_to be_a custom_class }
21
+
22
+ specify { expect(module_instance).to be_a custom_module }
23
+ specify { expect(module_instance).not_to be_a custom_class }
24
+
25
+ specify { expect(class_instance).to be_a custom_module }
26
+ specify { expect(class_instance).to be_a custom_class }
27
+ specify { expect(class_instance).not_to be_a String }
28
+
29
+ specify { expect(subclass_instance).to be_a custom_module }
30
+ specify { expect(subclass_instance).to be_a custom_class }
31
+ specify { expect(subclass_instance).to be_a custom_subclass }
32
+ specify { expect(subclass_instance).not_to be_a String }
33
+ end # describe
34
+
35
+ describe "with nil" do
36
+ specify { expect(nil).to be_a nil }
37
+ specify { expect(string).not_to be_a nil }
38
+ end # describe
39
+
40
+ describe "with array of types" do
41
+ specify { expect(nil).to be_a [String, nil] }
42
+ specify { expect(nil).not_to be_a [custom_class, String] }
43
+
44
+ specify { expect(string).to be_a [custom_module, String, nil] }
45
+ specify { expect(string).not_to be_a [custom_subclass, nil] }
46
+
47
+ specify { expect(subclass_instance).to be_a [custom_class, String] }
48
+ specify { expect(subclass_instance).not_to be_a [nil, String] }
49
+ end # describe
50
+ end # describe
@@ -0,0 +1,49 @@
1
+ # spec/matchers/construct_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "construct matcher" do
6
+ let :class_with_no_arguments do Class.new; end
7
+ let :class_with_arguments do
8
+ Class.new do def initialize(a, b, c = nil); end; end
9
+ end # let
10
+ let :not_a_class do Object.new; end
11
+
12
+ specify { expect(class_with_no_arguments).to construct }
13
+ specify { expect(class_with_arguments).to construct }
14
+ specify { expect(not_a_class).not_to construct }
15
+
16
+ describe "with a fixed number of arguments" do
17
+ specify { expect(class_with_no_arguments).to construct.
18
+ with(0).arguments }
19
+ specify { expect(class_with_no_arguments).not_to construct.
20
+ with(1).arguments }
21
+
22
+ specify { expect(class_with_arguments).not_to construct.
23
+ with(1).arguments }
24
+ specify { expect(class_with_arguments).to construct.
25
+ with(2).arguments }
26
+ specify { expect(class_with_arguments).to construct.
27
+ with(3).arguments }
28
+ specify { expect(class_with_arguments).not_to construct.
29
+ with(4).arguments }
30
+ end # describe
31
+
32
+ describe "with a range of arguments" do
33
+ specify { expect(class_with_no_arguments).to construct.
34
+ with(0..0).arguments }
35
+ specify { expect(class_with_no_arguments).not_to construct.
36
+ with(0..1).arguments }
37
+ specify { expect(class_with_no_arguments).not_to construct.
38
+ with(1..2).arguments }
39
+
40
+ specify { expect(class_with_arguments).not_to construct.
41
+ with(1..4).arguments }
42
+ specify { expect(class_with_arguments).not_to construct.
43
+ with(1..3).arguments }
44
+ specify { expect(class_with_arguments).not_to construct.
45
+ with(2..4).arguments }
46
+ specify { expect(class_with_arguments).to construct.
47
+ with(2..3).arguments }
48
+ end # describe
49
+ end # describe
@@ -0,0 +1,158 @@
1
+ # spec/matchers/respond_to_spec.rb
2
+
3
+ require 'spec_helper'
4
+
5
+ describe RSpec::Matchers::BuiltIn::RespondTo do
6
+ let :described_class do
7
+ Class.new do
8
+ def method_with_no_arguments; end
9
+
10
+ def method_with_required_arguments(a, b, c); end
11
+
12
+ def method_with_optional_arguments(a, b, c = nil, d = nil); end
13
+
14
+ def method_with_variadic_arguments(a, b, c, *rest); end
15
+
16
+ def method_with_mixed_arguments(a, b, c = nil, d = nil, *rest); end
17
+
18
+ def method_with_yield; yield; end
19
+
20
+ def method_with_block_argument(&block); end
21
+
22
+ def method_with_block_and_mixed_arguments(a, b = nil, *rest, &block); end
23
+ end # class
24
+ end # let
25
+ let :instance do described_class.new; end
26
+
27
+ specify { expect(instance).to respond_to :method_with_no_arguments }
28
+ specify { expect(instance).to respond_to :method_with_required_arguments }
29
+ specify { expect(instance).to respond_to :method_with_optional_arguments }
30
+ specify { expect(instance).to respond_to :method_with_variadic_arguments }
31
+ specify { expect(instance).to respond_to :method_with_yield }
32
+ specify { expect(instance).to respond_to :method_with_mixed_arguments }
33
+ specify { expect(instance).to respond_to :method_with_block_argument }
34
+
35
+ specify { expect(instance).not_to respond_to :not_a_method }
36
+
37
+ describe "with a fixed number of arguments" do
38
+ specify { expect(instance).to respond_to(:method_with_no_arguments).
39
+ with(0).arguments }
40
+ specify { expect(instance).not_to respond_to(:method_with_no_arguments).
41
+ with(1).arguments }
42
+
43
+ specify { expect(instance).not_to respond_to(:method_with_required_arguments).
44
+ with(2).arguments }
45
+ specify { expect(instance).to respond_to(:method_with_required_arguments).
46
+ with(3).arguments }
47
+ specify { expect(instance).not_to respond_to(:method_with_required_arguments).
48
+ with(4).arguments }
49
+
50
+ specify { expect(instance).not_to respond_to(:method_with_optional_arguments).
51
+ with(1).arguments }
52
+ specify { expect(instance).to respond_to(:method_with_optional_arguments).
53
+ with(2).arguments }
54
+ specify { expect(instance).to respond_to(:method_with_optional_arguments).
55
+ with(3).arguments }
56
+ specify { expect(instance).to respond_to(:method_with_optional_arguments).
57
+ with(4).arguments }
58
+ specify { expect(instance).not_to respond_to(:method_with_optional_arguments).
59
+ with(5).arguments }
60
+
61
+ specify { expect(instance).not_to respond_to(:method_with_variadic_arguments).
62
+ with(2).arguments }
63
+ specify { expect(instance).to respond_to(:method_with_variadic_arguments).
64
+ with(3).arguments }
65
+ specify { expect(instance).to respond_to(:method_with_variadic_arguments).
66
+ with(4).arguments }
67
+ specify { expect(instance).to respond_to(:method_with_variadic_arguments).
68
+ with(9001).arguments } # IT'S OVER NINE THOUSAND!
69
+
70
+ specify { expect(instance).not_to respond_to(:method_with_mixed_arguments).
71
+ with(1).arguments }
72
+ specify { expect(instance).to respond_to(:method_with_mixed_arguments).
73
+ with(2).arguments }
74
+ specify { expect(instance).to respond_to(:method_with_mixed_arguments).
75
+ with(3).arguments }
76
+ specify { expect(instance).to respond_to(:method_with_mixed_arguments).
77
+ with(4).arguments }
78
+ specify { expect(instance).to respond_to(:method_with_mixed_arguments).
79
+ with(5).arguments }
80
+ specify { expect(instance).to respond_to(:method_with_mixed_arguments).
81
+ with(9001).arguments } # WHAT?! NINE THOUSAND!
82
+ end # describe
83
+
84
+ describe "with a range of arguments" do
85
+ specify { expect(instance).to respond_to(:method_with_no_arguments).
86
+ with(0..0).arguments }
87
+ specify { expect(instance).not_to respond_to(:method_with_no_arguments).
88
+ with(0..1).arguments }
89
+
90
+ specify { expect(instance).not_to respond_to(:method_with_required_arguments).
91
+ with(0..3).arguments }
92
+ specify { expect(instance).not_to respond_to(:method_with_required_arguments).
93
+ with(2..3).arguments }
94
+ specify { expect(instance).not_to respond_to(:method_with_required_arguments).
95
+ with(3..4).arguments }
96
+ specify { expect(instance).to respond_to(:method_with_required_arguments).
97
+ with(3..3).arguments }
98
+
99
+ specify { expect(instance).not_to respond_to(:method_with_optional_arguments).
100
+ with(0..4).arguments }
101
+ specify { expect(instance).not_to respond_to(:method_with_optional_arguments).
102
+ with(2..5).arguments }
103
+ specify { expect(instance).to respond_to(:method_with_optional_arguments).
104
+ with(2..2).arguments }
105
+ specify { expect(instance).to respond_to(:method_with_optional_arguments).
106
+ with(2..3).arguments }
107
+ specify { expect(instance).to respond_to(:method_with_optional_arguments).
108
+ with(2..4).arguments }
109
+ specify { expect(instance).to respond_to(:method_with_optional_arguments).
110
+ with(3..4).arguments }
111
+ specify { expect(instance).to respond_to(:method_with_optional_arguments).
112
+ with(4..4).arguments }
113
+
114
+ specify { expect(instance).not_to respond_to(:method_with_variadic_arguments).
115
+ with(0..4).arguments }
116
+ specify { expect(instance).not_to respond_to(:method_with_variadic_arguments).
117
+ with(2..4).arguments }
118
+ specify { expect(instance).to respond_to(:method_with_variadic_arguments).
119
+ with(3..4).arguments }
120
+ specify { expect(instance).to respond_to(:method_with_variadic_arguments).
121
+ with(5..9).arguments }
122
+ specify { expect(instance).to respond_to(:method_with_variadic_arguments).
123
+ with(3..9001).arguments } # VEGETA, WHAT DOES...NEVER MIND, JOKE'S OVER
124
+
125
+ specify { expect(instance).not_to respond_to(:method_with_mixed_arguments).
126
+ with(0..5).arguments }
127
+ specify { expect(instance).not_to respond_to(:method_with_mixed_arguments).
128
+ with(1..5).arguments }
129
+ specify { expect(instance).to respond_to(:method_with_mixed_arguments).
130
+ with(2..5).arguments }
131
+ specify { expect(instance).to respond_to(:method_with_mixed_arguments).
132
+ with(3..5).arguments }
133
+ specify { expect(instance).to respond_to(:method_with_mixed_arguments).
134
+ with(4..5).arguments }
135
+ specify { expect(instance).to respond_to(:method_with_mixed_arguments).
136
+ with(5..5).arguments }
137
+ specify { expect(instance).to respond_to(:method_with_mixed_arguments).
138
+ with(2..9001).arguments } # THERE'S NO WAY THAT CAN BE RIGHT. CAN IT?!?
139
+ end # describe
140
+
141
+ describe "with a block" do
142
+ specify { expect(instance).not_to respond_to(:method_with_no_arguments).
143
+ with.a_block }
144
+
145
+ specify { expect(instance).not_to respond_to(:method_with_yield).
146
+ with.a_block }
147
+
148
+ specify { expect(instance).to respond_to(:method_with_block_argument).
149
+ with.a_block }
150
+ specify { expect(instance).to respond_to(:method_with_block_argument).
151
+ with(0).arguments.and.a_block }
152
+
153
+ specify { expect(instance).to respond_to(:method_with_block_and_mixed_arguments).
154
+ with.a_block }
155
+ specify { expect(instance).to respond_to(:method_with_block_and_mixed_arguments).
156
+ with(2..9001).arguments.and.a_block } # FINE, FINE. KILLJOYS.
157
+ end # describe
158
+ end # describe
@@ -0,0 +1,81 @@
1
+ # spec/controllers/text_controller_helper.rb
2
+
3
+ require 'mithril/controllers/abstract_controller_helper'
4
+
5
+ shared_examples_for "Mithril::Controllers::_TextController" do
6
+ it_behaves_like Mithril::Controllers::AbstractController
7
+
8
+ let :input do "text input"; end
9
+
10
+ describe :parser do
11
+ specify { expect(instance.parser).to be_a Mithril::Parsers::SimpleParser }
12
+ end # describe
13
+
14
+ describe :parse_command do
15
+ specify { expect(instance.parse_command input).to be_a Array }
16
+
17
+ specify "returns nil" do
18
+ command, args = instance.parse_command input
19
+ expect(command).to be nil
20
+ end # specify
21
+ end # describe
22
+
23
+ describe :command_missing do
24
+ specify "returns the text with a helpful message" do
25
+ output = instance.command_missing input
26
+ expect(output).to match /don't know how/
27
+ expect(output).to match /#{input}/
28
+ end # specify
29
+ end # describe
30
+
31
+ describe :invoke_command do
32
+ specify "returns the text with a helpful message" do
33
+ output = instance.invoke_command input
34
+ expect(output).to match /don't know how/
35
+ expect(output).to match /#{input}/
36
+ end # specify
37
+ end # describe
38
+
39
+ context "with actions defined" do
40
+ let :command do FactoryGirl.generate :action_key; end
41
+ let :arguments do %w(arguments for command); end
42
+
43
+ before :each do
44
+ described_class.define_action command do |session, args| args.join(' '); end
45
+ end # before each
46
+
47
+ specify { expect(instance.can_invoke? command.to_s).to be true }
48
+
49
+ describe :invoke_command do
50
+ let :text do "#{command} #{arguments.join(' ')}"; end
51
+
52
+ specify "invokes matching action" do
53
+ instance.should_receive(:invoke_action).with(command, arguments).and_call_original
54
+ instance.should_receive(:"action_#{command}").with(request.session, arguments).and_call_original
55
+ instance.invoke_command text
56
+ end # specify
57
+
58
+ specify { expect(instance.invoke_command text).to eq arguments.join(' ') }
59
+
60
+ context "with a non-matching command" do
61
+ let :non_matching do FactoryGirl.generate :action_key; end
62
+
63
+ specify { expect(instance.invoke_command non_matching.to_s).to match /don't know how/i }
64
+ end # context
65
+ end # describe
66
+ end # context
67
+
68
+ describe "empty actions" do
69
+ def self.input
70
+ chars, words = [*"a".."z", *"0".."9"], [""] * 5
71
+ 5.times do |i|
72
+ word = ""
73
+ (6 + rand(10)).times do word << chars[rand(36)]; end
74
+ words[i] = word
75
+ end # times
76
+ words.join " "
77
+ end # helper input
78
+
79
+ it_behaves_like "Mithril::Controllers::AbstractController#empty_actions", input
80
+ end # describe
81
+ end # describe