controll 0.2.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 (128) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +19 -0
  4. data/Gemfile.lock +134 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +320 -0
  7. data/Rakefile +49 -0
  8. data/VERSION +1 -0
  9. data/controll.gemspec +184 -0
  10. data/lib/controll.rb +14 -0
  11. data/lib/controll/assistant.rb +19 -0
  12. data/lib/controll/command.rb +24 -0
  13. data/lib/controll/commander.rb +24 -0
  14. data/lib/controll/errors.rb +1 -0
  15. data/lib/controll/executor.rb +6 -0
  16. data/lib/controll/executor/base.rb +16 -0
  17. data/lib/controll/executor/notificator.rb +12 -0
  18. data/lib/controll/flow_handler.rb +11 -0
  19. data/lib/controll/flow_handler/base.rb +19 -0
  20. data/lib/controll/flow_handler/control.rb +85 -0
  21. data/lib/controll/flow_handler/errors.rb +5 -0
  22. data/lib/controll/flow_handler/event_helper.rb +18 -0
  23. data/lib/controll/flow_handler/redirect.rb +51 -0
  24. data/lib/controll/flow_handler/redirect/action.rb +45 -0
  25. data/lib/controll/flow_handler/redirect/mapper.rb +41 -0
  26. data/lib/controll/flow_handler/render.rb +51 -0
  27. data/lib/controll/helper.rb +101 -0
  28. data/lib/controll/helper/event_matcher.rb +21 -0
  29. data/lib/controll/helper/hash_access.rb +26 -0
  30. data/lib/controll/helper/notify.rb +74 -0
  31. data/lib/controll/helper/params.rb +20 -0
  32. data/lib/controll/helper/path_resolver.rb +45 -0
  33. data/lib/controll/helper/session.rb +20 -0
  34. data/lib/controll/notify.rb +7 -0
  35. data/lib/controll/notify/base.rb +75 -0
  36. data/lib/controll/notify/flash.rb +52 -0
  37. data/lib/controll/notify/typed.rb +39 -0
  38. data/spec/acceptance/app_test.rb +156 -0
  39. data/spec/app/.gitignore +15 -0
  40. data/spec/app/Gemfile +6 -0
  41. data/spec/app/Gemfile.lock +108 -0
  42. data/spec/app/README.rdoc +261 -0
  43. data/spec/app/Rakefile +7 -0
  44. data/spec/app/app/controllers/application_controller.rb +6 -0
  45. data/spec/app/app/controllers/posts_controller.rb +52 -0
  46. data/spec/app/app/models/.gitkeep +0 -0
  47. data/spec/app/app/models/post.rb +88 -0
  48. data/spec/app/app/views/layouts/application.html.erb +13 -0
  49. data/spec/app/app/views/posts/_form.html.erb +31 -0
  50. data/spec/app/app/views/posts/edit.html.erb +6 -0
  51. data/spec/app/app/views/posts/index.html.erb +23 -0
  52. data/spec/app/app/views/posts/new.html.erb +5 -0
  53. data/spec/app/app/views/posts/show.html.erb +8 -0
  54. data/spec/app/config.ru +4 -0
  55. data/spec/app/config/application.rb +16 -0
  56. data/spec/app/config/boot.rb +6 -0
  57. data/spec/app/config/environment.rb +5 -0
  58. data/spec/app/config/environments/development.rb +21 -0
  59. data/spec/app/config/environments/test.rb +29 -0
  60. data/spec/app/config/initializers/backtrace_silencers.rb +7 -0
  61. data/spec/app/config/initializers/inflections.rb +15 -0
  62. data/spec/app/config/initializers/mime_types.rb +5 -0
  63. data/spec/app/config/initializers/secret_token.rb +7 -0
  64. data/spec/app/config/initializers/session_store.rb +8 -0
  65. data/spec/app/config/locales/en.yml +5 -0
  66. data/spec/app/config/routes.rb +62 -0
  67. data/spec/app/db/seeds.rb +7 -0
  68. data/spec/app/lib/assets/.gitkeep +0 -0
  69. data/spec/app/lib/tasks/.gitkeep +0 -0
  70. data/spec/app/log/.gitkeep +0 -0
  71. data/spec/app/public/404.html +26 -0
  72. data/spec/app/public/422.html +26 -0
  73. data/spec/app/public/500.html +25 -0
  74. data/spec/app/public/favicon.ico +0 -0
  75. data/spec/app/public/index.html +241 -0
  76. data/spec/app/public/javascripts/application.js +9663 -0
  77. data/spec/app/public/robots.txt +5 -0
  78. data/spec/app/public/stylesheets/application.css +83 -0
  79. data/spec/app/script/rails +6 -0
  80. data/spec/app/spec/controllers/posts_controller_spec.rb +59 -0
  81. data/spec/app/spec/isolated_spec_helper.rb +6 -0
  82. data/spec/app/spec/spec_helper.rb +5 -0
  83. data/spec/app/spec/unit/controllers/posts_controller_isolated_spec.rb +63 -0
  84. data/spec/app/spec/unit/controllers/posts_controller_spec.rb +59 -0
  85. data/spec/app/test/functional/.gitkeep +0 -0
  86. data/spec/app/test/functional/posts_controller_test.rb +67 -0
  87. data/spec/app/test/isolated_test_helper.rb +7 -0
  88. data/spec/app/test/test_helper.rb +6 -0
  89. data/spec/app/test/unit/.gitkeep +0 -0
  90. data/spec/app/test/unit/controllers/posts_controller_isolated_test.rb +71 -0
  91. data/spec/app/test/unit/controllers/posts_controller_test.rb +67 -0
  92. data/spec/app/vendor/assets/javascripts/.gitkeep +0 -0
  93. data/spec/app/vendor/assets/stylesheets/.gitkeep +0 -0
  94. data/spec/app/vendor/plugins/.gitkeep +0 -0
  95. data/spec/controll/asssistant_spec.rb +5 -0
  96. data/spec/controll/command_spec.rb +56 -0
  97. data/spec/controll/commander_spec.rb +68 -0
  98. data/spec/controll/executor/notificator_spec.rb +27 -0
  99. data/spec/controll/flow_handler/control_spec.rb +159 -0
  100. data/spec/controll/flow_handler/redirect/action_spec.rb +48 -0
  101. data/spec/controll/flow_handler/redirect/mapper_spec.rb +69 -0
  102. data/spec/controll/flow_handler/redirect_spec.rb +93 -0
  103. data/spec/controll/flow_handler/render_spec.rb +110 -0
  104. data/spec/controll/helper/event_matcher_spec.rb +21 -0
  105. data/spec/controll/helper/hash_access_spec.rb +25 -0
  106. data/spec/controll/helper/notify_spec.rb +48 -0
  107. data/spec/controll/helper/params_spec.rb +28 -0
  108. data/spec/controll/helper/path_resolver_spec.rb +49 -0
  109. data/spec/controll/helper/session_spec.rb +28 -0
  110. data/spec/controll/helper_spec.rb +108 -0
  111. data/spec/controll/notify/base_spec.rb +123 -0
  112. data/spec/controll/notify/flash_spec.rb +27 -0
  113. data/spec/controll/notify/message_handler.rb +72 -0
  114. data/spec/controll/notify/typed_spec.rb +28 -0
  115. data/spec/functional_test_helper.rb +25 -0
  116. data/spec/helper.rb +33 -0
  117. data/spec/rspec_controller_class.rb +15 -0
  118. data/spec/rspec_functional_helper.rb +25 -0
  119. data/spec/rspec_helper.rb +42 -0
  120. data/spec/spec_helper.rb +11 -0
  121. data/spec/test_helper.rb +183 -0
  122. data/spec/unit/functional_test_helper_test.rb +65 -0
  123. data/spec/unit/macros_test.rb +43 -0
  124. data/spec/unit/mixin_test.rb +147 -0
  125. data/spec/unit/rspec_functional_helper.rb +42 -0
  126. data/spec/unit/rspec_helper_test.rb +91 -0
  127. data/spec/unit/test_helper_test.rb +235 -0
  128. metadata +289 -0
@@ -0,0 +1,20 @@
1
+ module Controll
2
+ module Helper
3
+ module Params
4
+ extend ActiveSupport::Concern
5
+ include Controll::Helper::HashAccess
6
+
7
+ module ClassMethods
8
+ def param_methods *args
9
+ options = args.extract_options!
10
+ names = args
11
+ hash_access_method *names, options.merge(hash: :params)
12
+ end
13
+
14
+ def param_method name, options = {}
15
+ hash_access_method name, options.merge(hash: :params)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,45 @@
1
+ module Controll
2
+ module Helper
3
+ class PathResolver
4
+ class PathResolverError < StandardError; end
5
+
6
+ attr_reader :caller
7
+
8
+ def initialize caller
9
+ @caller = caller
10
+ end
11
+
12
+ def extract_path type, *args
13
+ if args.first.kind_of?(Symbol)
14
+ raise "Caller must have a notice method" unless caller.respond_to? :notice
15
+ caller.notice args.first
16
+ resolve_path type
17
+ else
18
+ args.empty? ? resolve_path(type) : args.first
19
+ end
20
+ end
21
+
22
+ def resolve_path type
23
+ raise "Caller must have a #main_event method" unless caller.respond_to? :main_event
24
+ caller.send(type).each do |path, events|
25
+ return path.to_s if matches? events
26
+ end
27
+ raise PathResolverError, "Path could not be resolved for: #{event_name}"
28
+ end
29
+
30
+ protected
31
+
32
+ def matches? events
33
+ event_matcher.match?(events)
34
+ end
35
+
36
+ def event_matcher
37
+ @event_matcher ||= EventMatcher.new event
38
+ end
39
+
40
+ def event
41
+ @event ||= caller.main_event
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,20 @@
1
+ module Controll
2
+ module Helper
3
+ module Session
4
+ extend ActiveSupport::Concern
5
+ include Controll::Helper::HashAccess
6
+
7
+ module ClassMethods
8
+ def session_methods *args
9
+ options = args.extract_options!
10
+ names = args
11
+ hash_access_method *names, options.merge(hash: :session)
12
+ end
13
+
14
+ def session_method name, options = {}
15
+ hash_access_method name, options.merge(hash: :session)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ module Controll
2
+ module Notify
3
+ autoload :Base, 'controll/notify/base'
4
+ autoload :Flash, 'controll/notify/flash'
5
+ autoload :Typed, 'controll/notify/typed'
6
+ end
7
+ end
@@ -0,0 +1,75 @@
1
+ require 'liquid'
2
+ require 'controll/notify/flash'
3
+
4
+ module Controll
5
+ module Notify
6
+ class Base < Flash
7
+ class NotifyMappingError < StandardError; end
8
+
9
+ def notify name, options = {}
10
+ @options.merge! options if options.kind_of? Hash
11
+ signal notify_msg(name)
12
+ end
13
+
14
+ def self.inherited(base)
15
+ base.extend ClassMethods
16
+ end
17
+
18
+ module ClassMethods
19
+ attr_reader :signal_type
20
+
21
+ def type name
22
+ @signal_type = name
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def notify_msg name, opts = {}
29
+ opts = options.merge(opts).stringify_keys
30
+
31
+ msg = send(name) if respond_to? name
32
+ msg ||= messages[name.to_sym] if respond_to? :messages
33
+ msg ||= name.to_sym
34
+
35
+ # if name is not mapped to a message, it could well be, that the
36
+ # event should not generate a nofification message
37
+ return nil if !msg
38
+
39
+ # try various approaches!
40
+ case msg
41
+ when Symbol
42
+ translate msg, opts
43
+ when String
44
+ msg.strip!
45
+ return replace_args(msg, opts) if msg =~ /{{.*}}/
46
+ msg
47
+ else
48
+ msg_error!
49
+ end
50
+ rescue
51
+ end
52
+
53
+ def msg_error!
54
+ raise NotifyMappingError, "Notify message could not be generated for: #{name}"
55
+ end
56
+
57
+ def translate msg, opts = {}
58
+ I18n.t i18n_key(msg), opts.symbolize_keys
59
+ end
60
+
61
+ def replace_args msg, opts
62
+ # Parses and compiles the template
63
+ Liquid::Template.parse(msg).render(opts)
64
+ end
65
+
66
+ def i18n_key msg
67
+ parts = self.class.name.split('::')
68
+ middle = parts[1..-2].join('.').underscore
69
+ type = parts.last.sub(/Msg$/, '').underscore
70
+ ns = [middle, type].join('.').sub /^./, ''
71
+ [ns,msg].join('.')
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,52 @@
1
+ module Controll
2
+ module Notify
3
+ class Flash
4
+ attr_reader :controller
5
+
6
+ def initialize controller, options = {}
7
+ @controller = controller
8
+ @options = options if options.kind_of? Hash
9
+ end
10
+
11
+ protected
12
+
13
+ def options
14
+ @options ||= {}
15
+ end
16
+
17
+ delegate :flash, to: :controller
18
+
19
+ class << self
20
+ attr_writer :types
21
+
22
+ def types
23
+ @types ||= [:notice, :error, :warning, :success]
24
+ end
25
+
26
+ def add_types *types
27
+ @types += types.flatten
28
+ end
29
+ alias_method :add_type, :add_types
30
+ end
31
+
32
+ def signal msg, type = nil
33
+ return if msg.blank?
34
+ type ||= signal_type || :notice
35
+ raise ArgumentError, "Unsupported flash type: #{type}. Register via #{self.class.name}#types or #add_type" unless valid_type? type
36
+ flash[type] = msg unless type.blank?
37
+ end
38
+
39
+ def valid_type? type
40
+ types.include? type.to_sym
41
+ end
42
+
43
+ def types
44
+ self.class.types
45
+ end
46
+
47
+ def signal_type
48
+ self.class.signal_type
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,39 @@
1
+ require 'controll/notify/flash'
2
+
3
+ module Controll
4
+ module Notify
5
+ class Typed < Flash
6
+ class MissingNotifyClass < StandardError; end
7
+
8
+ def notice
9
+ raise MissingNotifyClass, "#{msg_class(:notice)} class missing" unless msg_class(:notice)
10
+ @notice ||= msg_class(:notice).new flash #, options
11
+ end
12
+
13
+ def error
14
+ raise MissingNotifyClass, "#{msg_class(:warning)} class missing" unless msg_class(:error)
15
+ @error ||= msg_class(:error).new flash #, options
16
+ end
17
+
18
+ def success
19
+ raise MissingNotifyClass, "#{msg_class(:success)} class missing" unless msg_class(:success)
20
+ @success ||= msg_class(:success).new flash #, options
21
+ end
22
+
23
+ def warning
24
+ raise MissingNotifyClass, "#{msg_class(:warning)} class missing" unless msg_class(:warning)
25
+ @warning ||= msg_class(:warning).new flash #, options
26
+ end
27
+
28
+ protected
29
+
30
+ def msg_class name
31
+ msg_classes[name] ||= "#{self.class}::#{name.to_s.camelize}Msg".constantize
32
+ end
33
+
34
+ def msg_classes
35
+ @msg_classes ||= {}
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,156 @@
1
+ require 'helper'
2
+ require 'capybara'
3
+ require 'capybara_minitest_spec'
4
+ require 'capybara/poltergeist'
5
+ require 'socket'
6
+
7
+ module FocusedController
8
+ module Test
9
+ def self.port
10
+ @port ||= begin
11
+ server = TCPServer.new('127.0.0.1', 0)
12
+ port = server.addr[1]
13
+ ensure
14
+ server.close if server
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ Capybara.run_server = false
21
+ Capybara.app_host = "http://127.0.0.1:#{FocusedController::Test.port}"
22
+
23
+ describe 'acceptance test' do
24
+ def app_root
25
+ TEST_ROOT + '/app'
26
+ end
27
+
28
+ def within_test_app
29
+ Bundler.with_clean_env do
30
+ Dir.chdir(app_root) do
31
+ begin
32
+ prev_gemfile, ENV['BUNDLE_GEMFILE'] = ENV['BUNDLE_GEMFILE'], "#{app_root}/Gemfile"
33
+ prev_rubyopt, ENV['RUBYOPT'] = ENV['RUBYOPT'], nil
34
+ ENV['RAILS_VERSION'] = ActionPack::VERSION::STRING
35
+ yield
36
+ ensure
37
+ ENV['BUNDLE_GEMFILE'] = prev_gemfile
38
+ ENV['RUBYOPT'] = prev_rubyopt
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def run_without_bundle_exec(command)
45
+ within_test_app do
46
+ `#{command}`
47
+ $?.must_equal 0
48
+ end
49
+ end
50
+
51
+ def run_command(command)
52
+ run_without_bundle_exec "bundle exec #{command}"
53
+ end
54
+
55
+ def read_output(stream)
56
+ read = IO.select([stream], [], [stream], 0.1)
57
+ output = ""
58
+ loop { output << stream.read_nonblock(1024) } if read
59
+ output
60
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, EOFError
61
+ output
62
+ end
63
+
64
+ # This spawns a server process to run the app under test,
65
+ # and then waits for it to successfully come up so we can
66
+ # actually run the test.
67
+ def start_server
68
+ within_test_app do
69
+ IO.popen("bundle exec rails s -p #{FocusedController::Test.port} 2>&1") do |out|
70
+ start = Time.now
71
+ started = false
72
+ output = ""
73
+ timeout = 60.0
74
+
75
+ while !started && !out.eof? && Time.now - start <= timeout
76
+ output << read_output(out)
77
+ sleep 0.1
78
+
79
+ begin
80
+ TCPSocket.new('127.0.0.1', FocusedController::Test.port)
81
+ rescue Errno::ECONNREFUSED
82
+ else
83
+ started = true
84
+ end
85
+ end
86
+
87
+ raise "Server failed to start:\n#{output}" unless started
88
+
89
+ yield
90
+
91
+ Process.kill('KILL', out.pid)
92
+ end
93
+ end
94
+ end
95
+
96
+ before do
97
+ run_without_bundle_exec "bundle check >/dev/null || bundle update >/dev/null"
98
+ end
99
+
100
+ let(:s) { Capybara::Session.new(:poltergeist, nil) }
101
+
102
+ it 'does basic CRUD actions successfully' do
103
+ start_server do
104
+ s.visit '/posts'
105
+
106
+ s.click_link 'New Post'
107
+ s.fill_in 'Title', :with => 'Hello world'
108
+ s.fill_in 'Body', :with => 'Omg, first post'
109
+ s.click_button 'Create Post'
110
+
111
+ s.click_link 'Back'
112
+ s.must_have_content 'Hello world'
113
+ s.must_have_content 'Omg, first post'
114
+
115
+ s.click_link 'Show'
116
+ s.must_have_content 'Hello world'
117
+ s.must_have_content 'Omg, first post'
118
+
119
+ s.click_link 'Edit'
120
+ s.fill_in 'Title', :with => 'Goodbye world'
121
+ s.fill_in 'Body', :with => 'Omg, edited'
122
+ s.click_button 'Update Post'
123
+ s.must_have_content 'Goodbye world'
124
+ s.must_have_content 'Omg, edited'
125
+
126
+ s.click_link 'Back'
127
+ s.click_link 'Destroy'
128
+ s.wont_have_content 'Goodbye world'
129
+ s.wont_have_content 'Omg, edited'
130
+ end
131
+ end
132
+
133
+ it 'runs a functional test' do
134
+ run_command "ruby -Itest test/functional/posts_controller_test.rb"
135
+ end
136
+
137
+ it 'runs a unit test' do
138
+ run_command "ruby -Itest test/unit/controllers/posts_controller_test.rb"
139
+ end
140
+
141
+ it 'runs an isolated unit test' do
142
+ run_command "ruby -Itest test/unit/controllers/posts_controller_isolated_test.rb"
143
+ end
144
+
145
+ it 'runs a functional spec' do
146
+ run_command "rspec spec/controllers/posts_controller_spec.rb"
147
+ end
148
+
149
+ it 'runs a unit spec' do
150
+ run_command "rspec spec/unit/controllers/posts_controller_spec.rb"
151
+ end
152
+
153
+ it 'runs an isolated unit spec' do
154
+ run_command "rspec spec/unit/controllers/posts_controller_isolated_spec.rb"
155
+ end
156
+ end
@@ -0,0 +1,15 @@
1
+ # See http://help.github.com/ignore-files/ for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile ~/.gitignore_global
6
+
7
+ # Ignore bundler config
8
+ /.bundle
9
+
10
+ # Ignore the default SQLite database.
11
+ /db/*.sqlite3
12
+
13
+ # Ignore all logfiles and tempfiles.
14
+ /log/*.log
15
+ /tmp
data/spec/app/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rails', ENV['RAILS_VERSION'] || '~> 3.2.1'
4
+ gem 'focused_controller', :path => '../..'
5
+ gem 'rspec'
6
+ gem 'rspec-rails'