artoo 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/.yardopts +10 -0
  4. data/Gemfile +9 -0
  5. data/Gemfile.lock +10 -7
  6. data/README.md +2 -2
  7. data/api/assets/javascripts/artoo/controllers/robot.js.coffee +2 -1
  8. data/api/public/core.js +3 -1
  9. data/artoo.gemspec +0 -1
  10. data/lib/artoo/adaptors/adaptor.rb +27 -6
  11. data/lib/artoo/adaptors/ardrone.rb +10 -2
  12. data/lib/artoo/adaptors/ardrone_navigation.rb +9 -2
  13. data/lib/artoo/adaptors/ardrone_video.rb +13 -5
  14. data/lib/artoo/adaptors/firmata.rb +10 -2
  15. data/lib/artoo/adaptors/loopback.rb +2 -1
  16. data/lib/artoo/adaptors/roomba.rb +12 -4
  17. data/lib/artoo/adaptors/sphero.rb +15 -3
  18. data/lib/artoo/api.rb +48 -5
  19. data/lib/artoo/api_route_helpers.rb +22 -3
  20. data/lib/artoo/basic.rb +3 -7
  21. data/lib/artoo/connection.rb +25 -7
  22. data/lib/artoo/delegator.rb +6 -16
  23. data/lib/artoo/device.rb +39 -10
  24. data/lib/artoo/device_event_client.rb +6 -0
  25. data/lib/artoo/drivers/ardrone.rb +5 -1
  26. data/lib/artoo/drivers/ardrone_navigation.rb +5 -1
  27. data/lib/artoo/drivers/ardrone_video.rb +9 -4
  28. data/lib/artoo/drivers/button.rb +7 -1
  29. data/lib/artoo/drivers/driver.rb +44 -4
  30. data/lib/artoo/drivers/led.rb +12 -1
  31. data/lib/artoo/drivers/motor.rb +12 -1
  32. data/lib/artoo/drivers/pinger.rb +10 -1
  33. data/lib/artoo/drivers/pinger2.rb +10 -1
  34. data/lib/artoo/drivers/roomba.rb +65 -17
  35. data/lib/artoo/drivers/servo.rb +12 -2
  36. data/lib/artoo/drivers/sphero.rb +19 -5
  37. data/lib/artoo/drivers/wiichuck.rb +7 -1
  38. data/lib/artoo/drivers/wiiclassic.rb +14 -5
  39. data/lib/artoo/drivers/wiidriver.rb +4 -1
  40. data/lib/artoo/events.rb +11 -4
  41. data/lib/artoo/ext/actor.rb +1 -1
  42. data/lib/artoo/ext/timers.rb +1 -1
  43. data/lib/artoo/main.rb +2 -13
  44. data/lib/artoo/master.rb +20 -2
  45. data/lib/artoo/port.rb +8 -3
  46. data/lib/artoo/robot.rb +45 -17
  47. data/lib/artoo/utility.rb +53 -9
  48. data/lib/artoo/version.rb +1 -1
  49. data/test/drivers/driver_test.rb +15 -0
  50. data/test/drivers/led_test.rb +4 -0
  51. data/test/utility_test.rb +35 -18
  52. data/test/utility_test_cases.rb +56 -0
  53. metadata +5 -16
@@ -26,50 +26,58 @@ module Artoo
26
26
 
27
27
  attr_reader :connections, :devices, :name
28
28
 
29
+ # Create new robot
30
+ # @param [Hash] params
31
+ # @option params [String] :name
32
+ # @option params [Collection] :connections
33
+ # @option params [Collection] :devices
29
34
  def initialize(params={})
30
35
  @name = params[:name] || "Robot #{random_string}"
31
36
  initialize_connections(params[:connections] || {})
32
37
  initialize_devices(params[:devices] || {})
33
38
  end
34
-
39
+
35
40
  class << self
36
41
  attr_accessor :device_types, :working_code, :running,
37
42
  :use_api, :api_host, :api_port
38
-
43
+
39
44
  def connection_types
40
45
  @@connection_types ||= []
41
46
  end
42
47
 
43
- # connection to some hardware that has one or more devices via some specific protocol
44
- # Example:
45
- # connection :arduino, :adaptor => :firmata, :port => '/dev/tty.usbmodemxxxxx'
48
+ # Connection to some hardware that has one or more devices via some specific protocol
49
+ # @example connection :arduino, :adaptor => :firmata, :port => '/dev/tty.usbmodemxxxxx'
50
+ # @param [String] name
51
+ # @param [Hash] params
46
52
  def connection(name, params = {})
47
53
  Celluloid::Logger.info "Registering connection '#{name}'..."
48
54
  self.connection_types << {:name => name}.merge(params)
49
55
  end
50
56
 
51
- # device that uses a connection to communicate
52
- # Example:
53
- # device :collision_detect, :driver => :switch, :pin => 3
57
+ # Device that uses a connection to communicate
58
+ # @example device :collision_detect, :driver => :switch, :pin => 3
59
+ # @param [String] name
60
+ # @param [Hash] params
54
61
  def device(name, params = {})
55
62
  Celluloid::Logger.info "Registering device '#{name}'..."
56
63
  self.device_types ||= []
57
64
  self.device_types << {:name => name}.merge(params)
58
65
  end
59
66
 
60
- # the work that needs to be performed
61
- # Example:
62
- # work do
67
+ # The work that needs to be performed
68
+ # @example
69
+ # work do
63
70
  # every(10.seconds) do
64
71
  # puts "hello, world"
65
72
  # end
66
73
  # end
74
+ # @param [block] work
67
75
  def work(&block)
68
76
  Celluloid::Logger.info "Preparing work..."
69
77
  self.working_code = block if block_given?
70
78
  end
71
79
 
72
- # activate RESTful api
80
+ # Activate RESTful api
73
81
  # Example:
74
82
  # api :host => '127.0.0.1', :port => '1234'
75
83
  def api(params = {})
@@ -79,10 +87,11 @@ module Artoo
79
87
  self.api_port = params[:port] || '4321'
80
88
  end
81
89
 
82
- # work can be performed by either:
90
+ # Work can be performed by either:
83
91
  # an existing instance
84
92
  # an array of existing instances
85
93
  # or, a new instance can be created
94
+ # @param [Robot] robot
86
95
  def work!(robot=nil)
87
96
  return if !test? && is_running?
88
97
  prepare_robots(robot)
@@ -95,6 +104,7 @@ module Artoo
95
104
  end
96
105
  end
97
106
 
107
+ # Prepare master robots for work
98
108
  def prepare_robots(robot=nil)
99
109
  if robot.respond_to?(:work)
100
110
  robots = [robot]
@@ -107,32 +117,39 @@ module Artoo
107
117
  Celluloid::Actor[:master] = Master.new(robots)
108
118
  end
109
119
 
120
+ # Master actor
110
121
  def master
111
122
  Celluloid::Actor[:master]
112
123
  end
113
124
 
125
+ # @return [Boolean] True if test env
114
126
  def test?
115
127
  ENV["ARTOO_TEST"] == 'true'
116
128
  end
117
129
 
130
+ # @return [Boolean] True if cli env
118
131
  def cli?
119
132
  ENV["ARTOO_CLI"] == 'true'
120
133
  end
121
134
 
135
+ # @return [Boolean] True if it's running
122
136
  def is_running?
123
137
  self.running ||= false
124
138
  self.running == true
125
139
  end
126
140
  end
127
141
 
142
+ # @return [String] Name without spaces and downcased
128
143
  def safe_name
129
144
  name.gsub(' ', '_').downcase
130
145
  end
131
146
 
147
+ # @return [String] Api Host
132
148
  def api_host
133
149
  self.class.api_host
134
150
  end
135
151
 
152
+ # @return [String] Api Port
136
153
  def api_port
137
154
  self.class.api_port
138
155
  end
@@ -145,48 +162,59 @@ module Artoo
145
162
  execute_working_code
146
163
  end
147
164
 
165
+ # pause the work
148
166
  def pause_work
149
167
  Logger.info "Pausing work..."
150
168
  current_instance.timers.pause
151
169
  end
152
170
 
171
+ # continue with the work
153
172
  def continue_work
154
173
  Logger.info "Continuing work..."
155
174
  current_instance.timers.continue
156
175
  end
157
176
 
177
+ # Terminate all connections
158
178
  def disconnect
159
179
  connections.each {|k, c| c.async.disconnect}
160
180
  end
161
181
 
182
+ # @return [Connection] default connection
162
183
  def default_connection
163
184
  connections.values.first
164
185
  end
165
186
 
187
+ # @return [Collection] connection types
166
188
  def connection_types
167
189
  current_class.connection_types
168
190
  end
169
191
 
192
+ # @return [Collection] device types
170
193
  def device_types
171
194
  current_class.device_types ||= []
172
195
  current_class.device_types
173
196
  end
174
197
 
198
+ # @return [Proc] current working code
175
199
  def working_code
176
200
  current_class.working_code ||= proc {puts "No work defined."}
177
201
  end
178
-
202
+
203
+ # @return [Hash] robot
179
204
  def to_hash
180
- {:name => name,
181
- :connections => connections.each_value.collect {|c|c.to_hash},
182
- :devices => devices.each_value.collect {|d|d.to_hash}
205
+ {
206
+ :name => name,
207
+ :connections => connections.each_value.collect {|c|c.to_hash},
208
+ :devices => devices.each_value.collect {|d|d.to_hash}
183
209
  }
184
210
  end
185
211
 
212
+ # @return [JSON] robot
186
213
  def as_json
187
214
  MultiJson.dump(to_hash)
188
215
  end
189
216
 
217
+ # @return [String] robot
190
218
  def inspect
191
219
  "#<Robot #{object_id}>"
192
220
  end
@@ -1,38 +1,82 @@
1
- require 'active_support/inflector'
2
-
3
1
  module Artoo
2
+ # Utility methods used for convertions
4
3
  module Utility
4
+
5
+ # Converts camel_cased_word to constant
6
+ # @example "CamelCasedWord" > CamelCasedWord
7
+ # @return [Constant] Converted constant
5
8
  def constantize(camel_cased_word)
6
- ActiveSupport::Inflector.constantize(camel_cased_word)
9
+ names = camel_cased_word.split('::')
10
+ names.shift if names.empty? || names.first.empty?
11
+
12
+ names.inject(Object) do |constant, name|
13
+ if constant == Object
14
+ constant.const_get(name)
15
+ else
16
+ candidate = constant.const_get(name)
17
+ next candidate if constant.const_defined?(name, false)
18
+ next candidate unless Object.const_defined?(name)
19
+
20
+ # Go down the ancestors to check it it's owned
21
+ # directly before we reach Object or the end of ancestors.
22
+ constant = constant.ancestors.inject do |const, ancestor|
23
+ break const if ancestor == Object
24
+ break ancestor if ancestor.const_defined?(name, false)
25
+ const
26
+ end
27
+
28
+ # owner is in Object, so raise
29
+ constant.const_get(name, false)
30
+ end
31
+ end
7
32
  end
8
33
 
9
- def classify(underscored)
10
- ActiveSupport::Inflector.camelize(underscored.to_s.sub(/.*\./, ''))
34
+ # Convert a underscore string to a class
35
+ # @example "underscore_word" > UnderscoreWord
36
+ # @return [Class] converted class
37
+ def classify(word)
38
+ underscore(word).gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
11
39
  end
12
40
 
41
+ # @return [String] random string
13
42
  def random_string
14
43
  (0...8).map{65.+(rand(26)).chr}.join
15
44
  end
16
45
 
46
+ # Converts camel cased word to downcased with undercores
47
+ # @example CamelCase > camel_case
48
+ def underscore(camel_cased_word)
49
+ word = camel_cased_word.to_s.dup
50
+ word.gsub!(/::/, '/')
51
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
52
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
53
+ word.tr!("-", "_")
54
+ word.downcase!
55
+ word
56
+ end
57
+
58
+ # @return [Celluloid::Actor] current instance
17
59
  def current_instance
18
60
  Celluloid::Actor.current
19
61
  end
20
62
 
63
+ # @return [Class] current actor class
21
64
  def current_class
22
65
  Celluloid::Actor.current.class
23
66
  end
24
67
  end
25
68
  end
26
69
 
27
- # just a bit of syntactic sugar, actually does nothing
28
- # Example:
29
- # 20.seconds => 20
30
- # 1.second => 1
70
+ # Adds syntactic suger for seconds
31
71
  class Integer < Numeric
72
+ # @return [Integer] Plain number, actually does nothing
73
+ # @example 20.seconds => 20
32
74
  def seconds
33
75
  return self
34
76
  end
35
77
 
78
+ # @return [Integer] Plain number, actually does nothing
79
+ # @example 1.second => 1
36
80
  def second
37
81
  return self
38
82
  end
@@ -1,5 +1,5 @@
1
1
  module Artoo
2
2
  unless const_defined?('VERSION')
3
- VERSION = "0.4.0"
3
+ VERSION = "0.4.1"
4
4
  end
5
5
  end
@@ -1,6 +1,10 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
2
  require 'artoo/drivers/driver'
3
3
 
4
+ class Awesome < Artoo::Drivers::Driver
5
+ COMMANDS = [:awesome].freeze
6
+ end
7
+
4
8
  describe Artoo::Drivers::Driver do
5
9
  before do
6
10
  @device = mock('device')
@@ -18,4 +22,15 @@ describe Artoo::Drivers::Driver do
18
22
  @device.expects(:pin).returns(@pin)
19
23
  @driver.pin.must_equal @pin
20
24
  end
25
+
26
+ it 'Driver#commands' do
27
+ @awesome_driver = Awesome.new(:parent => @device)
28
+ @awesome_driver.commands.first.must_equal :awesome
29
+ end
30
+
31
+ it 'Driver#known_command?' do
32
+ @awesome_driver = Awesome.new(:parent => @device)
33
+ @awesome_driver.known_command?(:awesome).must_equal true
34
+ @awesome_driver.known_command?(:crazy).must_equal false
35
+ end
21
36
  end
@@ -49,4 +49,8 @@ describe Artoo::Drivers::Led do
49
49
  @connection.expects(:analog_write).with(@pin, val)
50
50
  @led.brightness(val)
51
51
  end
52
+
53
+ it 'Led#commands' do
54
+ @led.commands.must_include :toggle
55
+ end
52
56
  end
@@ -1,27 +1,44 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/test_helper")
2
-
3
- class UtilityTestRobot < Artoo::Robot
4
- connection :test_connection
5
- device :test_device_1
6
- device :test_device_2
7
- end
2
+ require File.expand_path(File.dirname(__FILE__) + "/utility_test_cases")
8
3
 
9
4
  describe Artoo::Utility do
10
- before do
11
- @robot = UtilityTestRobot.new
5
+ include UtilityTestCases
6
+ include Artoo::Utility
7
+
8
+ describe "#random_string" do
9
+ it "returns an 8-character String" do
10
+ random_string.must_be_kind_of String
11
+ random_string.size.must_equal 8
12
+ end
12
13
  end
13
14
 
14
- it 'Artoo::Utility#random_string' do
15
- string = @robot.random_string
16
- string.must_be_kind_of String
17
- string.size.must_equal 8
15
+ describe "#classify" do
16
+ it "converts a snake_case string to CamelCase" do
17
+ classify("firmata").must_equal "Firmata"
18
+ classify("ardrone_awesome").must_equal "ArdroneAwesome"
19
+
20
+ CamelToUnderscore.each do |camel, underscore|
21
+ classify(underscore).must_equal camel
22
+ end
23
+ end
18
24
  end
19
25
 
20
- it 'Artoo::Utility#classify' do
21
- string = @robot.classify('firmata')
22
- string.must_equal 'Firmata'
26
+ describe "#underscore" do
27
+ it "converts CamelCase strings to snake_case" do
28
+ CamelToUnderscore.each do |camel, underscore|
29
+ underscore(camel).must_equal underscore
30
+ end
23
31
 
24
- string = @robot.classify('ardrone_awesome')
25
- string.must_equal 'ArdroneAwesome'
32
+ underscore("HTMLTidy").must_equal "html_tidy"
33
+ underscore("HTMLTidyGenerator").must_equal "html_tidy_generator"
34
+ end
26
35
  end
27
- end
36
+
37
+ describe "#constantize" do
38
+ it "converts strings to constants" do
39
+ run_constantize_tests_on do |string|
40
+ constantize(string)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,56 @@
1
+ module Ace
2
+ module Base
3
+ class Case
4
+ class Dice
5
+ end
6
+ end
7
+ class Fase < Case
8
+ end
9
+ end
10
+ class Gas
11
+ include Base
12
+ end
13
+ end
14
+
15
+ class Object
16
+ module AddtlGlobalConstants
17
+ class Case
18
+ class Dice
19
+ end
20
+ end
21
+ end
22
+ include AddtlGlobalConstants
23
+ end
24
+
25
+ module UtilityTestCases
26
+ def run_constantize_tests_on
27
+ assert_equal Ace::Base::Case, yield("Ace::Base::Case")
28
+ assert_equal Ace::Base::Case, yield("::Ace::Base::Case")
29
+ assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Case::Dice")
30
+ assert_equal Ace::Base::Fase::Dice, yield("Ace::Base::Fase::Dice")
31
+ assert_equal Ace::Gas::Case, yield("Ace::Gas::Case")
32
+ assert_equal Ace::Gas::Case::Dice, yield("Ace::Gas::Case::Dice")
33
+ assert_equal Case::Dice, yield("Case::Dice")
34
+ assert_equal Case::Dice, yield("Object::Case::Dice")
35
+ assert_equal UtilityTestCases, yield("UtilityTestCases")
36
+ assert_equal UtilityTestCases, yield("::UtilityTestCases")
37
+ assert_equal Object, yield("")
38
+ assert_equal Object, yield("::")
39
+ assert_raises(NameError) { yield("UnknownClass") }
40
+ assert_raises(NameError) { yield("UnknownClass::Ace") }
41
+ assert_raises(NameError) { yield("UnknownClass::Ace::Base") }
42
+ assert_raises(NameError) { yield("An invalid string") }
43
+ assert_raises(NameError) { yield("InvalidClass\n") }
44
+ assert_raises(NameError) { yield("Ace::UtilityTestCases") }
45
+ assert_raises(NameError) { yield("Ace::Base::UtilityTestCases") }
46
+ assert_raises(NameError) { yield("Ace::Gas::Base") }
47
+ assert_raises(NameError) { yield("Ace::Gas::UtilityTestCases") }
48
+ end
49
+ end
50
+
51
+ CamelToUnderscore = {
52
+ "Product" => "product",
53
+ "SpecialGuest" => "special_guest",
54
+ "ApplicationController" => "application_controller",
55
+ "Area51Controller" => "area51_controller"
56
+ }