artoo 0.4.0 → 0.4.1

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 (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
+ }