whipped-cream 0.0.1pre1

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.
data/.cane ADDED
@@ -0,0 +1,3 @@
1
+ --abc-max 10
2
+ --color
3
+ --gte coverage/.last_run.json,95
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - ruby-head
6
+ matrix:
7
+ allow_failures:
8
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ ruby '1.9.3'
4
+
5
+ gemspec
6
+
7
+ group :development do
8
+ gem 'guard-cane'
9
+ gem 'guard-rspec'
10
+ end
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ guard :rspec, all_on_start: true, all_after_pass: true, env: { skip_coverage: true } do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
7
+ guard :cane do
8
+ watch(/.*\.rb/)
9
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Justin Campbell
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # whipped-cream
2
+
3
+ ## DSL
4
+
5
+ ```rb
6
+ name "Garage"
7
+
8
+ camera
9
+
10
+ button "Open/Close", pin: 1 do
11
+ tap
12
+ end
13
+
14
+ sensor "Door",
15
+ pin: 2,
16
+ low: "Closed",
17
+ high: "Open",
18
+ on_high: :door_opened
19
+
20
+ sensor "Temperature", pin: 3, unit: "F" do
21
+ binary_to_farenheit(value)
22
+ end
23
+
24
+ helpers do
25
+ def binary_to_farenheit(binary)
26
+ binary.to_f * 123.45
27
+ end
28
+
29
+ def door_opened
30
+ WhippedCream.find("Front Door").ring_bell
31
+ end
32
+ end
33
+ ```
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require 'cane/rake_task'
3
+ require 'rspec/core/rake_task'
4
+
5
+ task default: :ci
6
+
7
+ task ci: [:spec, :cane]
8
+
9
+ Cane::RakeTask.new
10
+ RSpec::Core::RakeTask.new
data/WELCOME ADDED
@@ -0,0 +1,14 @@
1
+
2
+ Thanks for installing whipped-cream! Please don't hesitate to report bugs
3
+ and feature requests at:
4
+
5
+ https://github.com/justincampbell/whipped-cream/issues
6
+
7
+ To run a demo, just do:
8
+
9
+ whipped-cream demo
10
+
11
+ Or use the help command to see more options:
12
+
13
+ whipped-cream help
14
+
data/bin/whipped-cream ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'whipped-cream'
6
+
7
+ WhippedCream::CLI.start
data/demo.rb ADDED
@@ -0,0 +1,9 @@
1
+ name "Demo"
2
+
3
+ button "Open/Close", pin: 1
4
+
5
+ sensor "Door", pin: 2, high: "Open", low: "Closed"
6
+
7
+ sensor "Ruby Version" do
8
+ RUBY_VERSION
9
+ end
@@ -0,0 +1,8 @@
1
+ require "whipped-cream/builder"
2
+ require "whipped-cream/button"
3
+ require "whipped-cream/cli"
4
+ require "whipped-cream/plugin"
5
+ require "whipped-cream/runner"
6
+ require "whipped-cream/sensor"
7
+ require "whipped-cream/server"
8
+ require "whipped-cream/version"
@@ -0,0 +1,60 @@
1
+ module WhippedCream
2
+ # Creates Plugin instances from a DSL
3
+ class Builder
4
+ def self.build(&block)
5
+ builder = new(&block)
6
+ builder.build
7
+ builder.plugin
8
+ end
9
+
10
+ def self.from_file(path)
11
+ contents = File.read(path)
12
+
13
+ from_string(contents)
14
+ end
15
+
16
+ def self.from_string(string)
17
+ builder = new(string)
18
+ builder.build
19
+ builder.plugin
20
+ end
21
+
22
+ attr_reader :block, :string
23
+
24
+ def initialize(string = nil, &block)
25
+ @block = block
26
+ @string = string
27
+ end
28
+
29
+ def build
30
+ @build ||= string ?
31
+ instance_eval(string) :
32
+ instance_eval(&block)
33
+ end
34
+
35
+ def plugin
36
+ @plugin ||= Plugin.new
37
+ end
38
+
39
+ def button(name, options = {}, &block)
40
+ options = options.merge({ block: block })
41
+ plugin.controls << Button.new(name, options)
42
+ end
43
+
44
+ def camera
45
+ plugin.camera = true
46
+ end
47
+
48
+ def helpers(&block)
49
+ plugin.instance_eval(&block)
50
+ end
51
+
52
+ def name(string)
53
+ plugin.name = string
54
+ end
55
+
56
+ def sensor(name, options = {}, &block)
57
+ plugin.controls << Sensor.new(name, options, &block)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,15 @@
1
+ require 'whipped-cream/control'
2
+
3
+ module WhippedCream
4
+ # A Button represents a one-time action, such as momentarily turning on a
5
+ # pin, or sending a message to an object
6
+ class Button < Control
7
+ attr_reader :name, :pin, :block
8
+
9
+ def initialize(name, options = {})
10
+ @name = name
11
+ @pin = options[:pin]
12
+ @block = options[:block]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,43 @@
1
+ require 'thor'
2
+
3
+ module WhippedCream
4
+ # The CLI gets invoked from the binary, and encapsulates all user interaction
5
+ # logic
6
+ class CLI < Thor
7
+ default_task :usage
8
+
9
+ desc "usage", "Display usage banner", hide: true
10
+ def usage
11
+ puts [
12
+ "whipped-cream #{WhippedCream::VERSION}",
13
+ "https://github.com/justincampbell/whipped-cream"
14
+ ].join("\n")
15
+
16
+ puts "\n"
17
+
18
+ help
19
+ end
20
+
21
+ desc "demo", "Start the demo plugin"
22
+ def demo
23
+ plugin = Plugin.from_file(File.expand_path('../../../demo.rb', __FILE__))
24
+ server = Server.new(plugin)
25
+ server.start
26
+ end
27
+
28
+ desc "start PLUGIN", "Start a plugin"
29
+ def start(plugin_name)
30
+ plugin_path = resolve_plugin(plugin_name)
31
+
32
+ plugin = Plugin.from_file(plugin_path)
33
+ server = Server.new(plugin)
34
+ server.start
35
+ end
36
+
37
+ no_tasks do
38
+ def resolve_plugin(name)
39
+ name # TODO: resolve name to filename
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,12 @@
1
+ module WhippedCream
2
+ # An abstract class for controls to inherit from
3
+ class Control
4
+ def id
5
+ name.downcase.gsub(/[^\w]+/, '_').gsub(/^_|_$/, '').to_sym
6
+ end
7
+
8
+ def type
9
+ self.class.to_s.split('::').last.downcase.to_sym
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ # Fake PiPiper
2
+ module PiPiper
3
+ # Just enough to imitate a pin
4
+ class Pin
5
+ attr_reader :pin, :direction, :value
6
+
7
+ def initialize(options)
8
+ @pin = options[:pin]
9
+ @direction = options[:direction]
10
+ end
11
+
12
+ def on
13
+ @value = :on
14
+
15
+ log "Pin #{pin} on"
16
+ end
17
+
18
+ def off
19
+ @value = :off
20
+
21
+ log "Pin #{pin} off"
22
+ end
23
+
24
+ def log(message)
25
+ puts message unless ENV['RUBY_ENV'] == 'test'
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ module WhippedCream
2
+ # Data representation of a Plugin
3
+ class Plugin
4
+ attr_accessor :camera, :name
5
+
6
+ def self.build(&block)
7
+ Builder.build(&block)
8
+ end
9
+
10
+ def self.from_file(path)
11
+ Builder.from_file(path)
12
+ end
13
+
14
+ def self.from_string(string)
15
+ Builder.from_string(string)
16
+ end
17
+
18
+ def controls
19
+ @controls ||= []
20
+ end
21
+
22
+ def buttons
23
+ controls.select { |control| control.is_a? Button }
24
+ end
25
+
26
+ def fields
27
+ controls.select { |control| control.is_a? Field }
28
+ end
29
+
30
+ def sensors
31
+ controls.select { |control| control.is_a? Sensor }
32
+ end
33
+
34
+ def switches
35
+ controls.select { |control| control.is_a? Switch }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ body {
2
+ background-color: white;
3
+ font-family: sans-serif;
4
+ font-size: 16pt;
5
+ }
6
+
7
+ a {
8
+ text-decoration: none;
9
+ }
@@ -0,0 +1,79 @@
1
+ begin
2
+ require 'pi_piper'
3
+ rescue LoadError
4
+ require 'whipped-cream/pi_piper'
5
+ end
6
+
7
+ module WhippedCream
8
+ # Actor that manages all interaction with a plugin
9
+ class Runner
10
+ def self.instance
11
+ @instance
12
+ end
13
+
14
+ def self.create_instance(plugin)
15
+ @instance = new(plugin)
16
+ end
17
+
18
+ attr_reader :plugin
19
+
20
+ def initialize(plugin)
21
+ @plugin = plugin
22
+
23
+ configure
24
+ end
25
+
26
+ def name
27
+ plugin.name
28
+ end
29
+
30
+ def pins
31
+ @pins ||= {}
32
+ end
33
+
34
+ private
35
+
36
+ def configure
37
+ configure_buttons
38
+ configure_sensors
39
+ end
40
+
41
+ def configure_buttons
42
+ plugin.buttons.each do |button|
43
+ create_pin button, direction: :out
44
+
45
+ define_singleton_method button.id do
46
+ pin = pins[button.id]
47
+
48
+ pin.on
49
+ sleep 0.25
50
+ pin.off
51
+ end
52
+ end
53
+ end
54
+
55
+ def configure_sensors
56
+ plugin.sensors.each do |sensor|
57
+ create_pin sensor, direction: :in
58
+
59
+ define_singleton_method sensor.id do
60
+ if sensor.pin
61
+ pin = pins[sensor.id]
62
+
63
+ pin.value == 1 ? sensor.high : sensor.low
64
+ else
65
+ sensor.block.call
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def create_pin(control, options = {})
72
+ return unless control.pin
73
+
74
+ options[:pin] = control.pin
75
+
76
+ pins[control.id] = PiPiper::Pin.new(options)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,21 @@
1
+ require 'whipped-cream/control'
2
+
3
+ module WhippedCream
4
+ # A Sensor displays the state of something, such as a pin's value, or the
5
+ # return value of the method. Sensors can also have callbacks associated with
6
+ # state changes.
7
+ class Sensor < Control
8
+ attr_reader :name, :pin, :low, :high, :on_low, :on_high, :block
9
+
10
+ def initialize(name, options = {}, &block)
11
+ @name = name
12
+ @pin = options[:pin]
13
+ @low = options[:low]
14
+ @high = options[:high]
15
+ @on_low = options[:on_low]
16
+ @on_high = options[:on_high]
17
+
18
+ @block = block
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,80 @@
1
+ require 'sinatra'
2
+
3
+ module WhippedCream
4
+ # A server handles building a plugin/runner and starting a web server
5
+ class Server
6
+ attr_reader :plugin
7
+
8
+ def initialize(plugin)
9
+ @plugin = plugin
10
+ end
11
+
12
+ def start
13
+ ensure_routes_built
14
+ ensure_runner_started
15
+
16
+ start_web
17
+ end
18
+
19
+ def runner
20
+ @runner ||= Runner.create_instance(plugin)
21
+ end
22
+
23
+ def port
24
+ 8080
25
+ end
26
+
27
+ def web
28
+ @web ||= Web
29
+ end
30
+
31
+ private
32
+
33
+ def ensure_runner_started
34
+ runner
35
+ end
36
+
37
+ def ensure_routes_built
38
+ @routes_built ||= build_routes || true
39
+ end
40
+
41
+ def start_web
42
+ Rack::Server.start app: web, port: port
43
+ end
44
+
45
+ def build_routes
46
+ build_button_routes
47
+ end
48
+
49
+ def build_button_routes
50
+ plugin.buttons.each do |button|
51
+ web.get "/#{button.id}" do
52
+ runner.send(button.id)
53
+ redirect to('/')
54
+ end
55
+ end
56
+ end
57
+
58
+ # A Sinatra application skeleton that is used to build up the web server
59
+ # for this plugin.
60
+ class Web < Sinatra::Application
61
+ get '/' do
62
+ erb :index
63
+ end
64
+
65
+ private
66
+
67
+ def controls
68
+ runner.plugin.controls
69
+ end
70
+
71
+ def runner
72
+ Runner.instance
73
+ end
74
+
75
+ def title
76
+ runner.name
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,3 @@
1
+ module WhippedCream
2
+ VERSION = "0.0.1pre1"
3
+ end
@@ -0,0 +1,3 @@
1
+ <form action="/<%= control.id %>" method="get">
2
+ <input type="submit" value="<%= control.name %>">
3
+ </form>
@@ -0,0 +1,4 @@
1
+ <% controls.each do |control| %>
2
+ <%= erb control.type, locals: { control: control } %>
3
+ <hr />
4
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE HTML>
2
+ <html>
3
+ <head>
4
+ <title><%= title %></title>
5
+ <link href="/assets/application.css" media="screen" rel="stylesheet" type="text/css">
6
+ <meta content="width=device-width" name="viewport">
7
+ </head>
8
+ <body>
9
+ <h1><%= title %></h1>
10
+ <%= yield %>
11
+ </body>
12
+ </html>
@@ -0,0 +1,2 @@
1
+ <h2><%= control.name %></h3>
2
+ <p><%= runner.send(control.id) %><p>
@@ -0,0 +1,139 @@
1
+ require 'spec_helper'
2
+
3
+ describe WhippedCream::Builder do
4
+ subject { plugin }
5
+
6
+ let(:plugin) {
7
+ described_class.build do
8
+ name "Garage"
9
+ end
10
+ }
11
+ let(:plugin_file_path) { "foo/bar/baz" }
12
+ let(:plugin_string) {
13
+ <<-PLUGIN
14
+ name "Garage"
15
+
16
+ button "Open/Close", pin: 1
17
+ PLUGIN
18
+ }
19
+
20
+ it "returns a plugin" do
21
+ should be_a(WhippedCream::Plugin)
22
+ end
23
+
24
+ context "with helper methods" do
25
+ subject {
26
+ described_class.build do
27
+ helpers do
28
+ def foo
29
+ :bar
30
+ end
31
+ end
32
+ end
33
+ }
34
+
35
+ it "defines the methods on the object" do
36
+ expect(subject.foo).to eq(:bar)
37
+ end
38
+ end
39
+
40
+ describe ".from_file" do
41
+ before do
42
+ File.stub read: plugin_string
43
+ end
44
+
45
+ it "reads the file" do
46
+ expect(File).to receive(:read).with(plugin_file_path)
47
+
48
+ described_class.from_file plugin_file_path
49
+ end
50
+
51
+ it "parses the contents" do
52
+ plugin = described_class.from_file(plugin_file_path)
53
+
54
+ expect(plugin.name).to eq("Garage")
55
+ expect(plugin.controls.first.name).to eq("Open/Close")
56
+ end
57
+ end
58
+
59
+ describe ".from_string" do
60
+ it "parses the string" do
61
+ plugin = described_class.from_string(plugin_string)
62
+
63
+ expect(plugin.name).to eq("Garage")
64
+ expect(plugin.controls.first.name).to eq("Open/Close")
65
+ end
66
+ end
67
+
68
+ describe "#camera" do
69
+ subject { plugin.camera }
70
+
71
+ it { should be_false }
72
+
73
+ context "with camera in the plugin" do
74
+ let(:plugin) {
75
+ described_class.build do
76
+ camera
77
+ end
78
+ }
79
+
80
+ it { should be_true }
81
+ end
82
+ end
83
+
84
+ describe "#button" do
85
+ subject { plugin.buttons }
86
+
87
+ it { should be_empty }
88
+
89
+ context "with a button" do
90
+ let(:plugin) {
91
+ described_class.build do
92
+ button "Open/Close", pin: 1 do
93
+ :tap
94
+ end
95
+ end
96
+ }
97
+
98
+ it "adds a button" do
99
+ expect(plugin.buttons).to have(1).item
100
+
101
+ button = plugin.buttons.first
102
+
103
+ expect(button.name).to eq("Open/Close")
104
+ expect(button.pin).to eq(1)
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "#sensor(s)" do
110
+ subject { plugin.sensors }
111
+
112
+ it { should be_empty }
113
+
114
+ context "with a sensor" do
115
+ let(:plugin) {
116
+ described_class.build do
117
+ sensor "Door",
118
+ pin: 2,
119
+ low: "Closed",
120
+ high: "Open",
121
+ on_high: :door_opened
122
+ end
123
+ }
124
+
125
+ it "adds a sensor" do
126
+ expect(plugin.sensors).to have(1).item
127
+
128
+ sensor = plugin.sensors.first
129
+
130
+ expect(sensor.name).to eq("Door")
131
+ expect(sensor.pin).to eq(2)
132
+ expect(sensor.low).to eq("Closed")
133
+ expect(sensor.high).to eq("Open")
134
+ expect(sensor.on_high).to eq(:door_opened)
135
+ expect(sensor.block).to be_nil
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe WhippedCream::Button do
4
+ subject(:button) { described_class.new(name, pin: pin, block: block) }
5
+
6
+ let(:name) { "Open/Close" }
7
+ let(:pin) { nil }
8
+ let(:block) { nil }
9
+
10
+ its(:name) { should eq(name) }
11
+ its(:id) { should eq(:open_close) }
12
+ its(:type) { should eq(:button) }
13
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ require 'fileutils'
4
+ require 'tmpdir'
5
+
6
+ describe WhippedCream::CLI do
7
+ subject { cli }
8
+ let(:cli) { described_class.new }
9
+
10
+ let(:plugin_filename) { File.join(tmpdir, "garage.rb") }
11
+ let(:plugin_string) {
12
+ <<-PLUGIN
13
+ name "Garage"
14
+
15
+ button "Open/Close", pin: 1
16
+ PLUGIN
17
+ }
18
+ let(:tmpdir) { Dir.mktmpdir }
19
+
20
+ before do
21
+ File.open(plugin_filename, 'w') { |file| file.write plugin_string }
22
+ end
23
+
24
+ after do
25
+ FileUtils.rm_rf tmpdir
26
+ end
27
+
28
+ describe "#demo" do
29
+ it "launches a web server with an example plugin" do
30
+ expect(Rack::Server).to receive(:start)
31
+
32
+ cli.demo
33
+ end
34
+ end
35
+
36
+ describe "#usage" do
37
+ it "displays a banner and help" do
38
+ expect(cli).to receive(:puts).exactly(2).times
39
+ expect(cli).to receive(:help)
40
+
41
+ cli.usage
42
+ end
43
+ end
44
+
45
+ describe "#start" do
46
+ it "starts a server for the plugin" do
47
+ expect(Rack::Server).to receive(:start)
48
+
49
+ cli.start(plugin_filename)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe WhippedCream::Plugin do
4
+ subject(:plugin) { described_class.new }
5
+
6
+ its(:camera) { should be_nil }
7
+ its(:name) { should be_nil }
8
+
9
+ its(:controls) { should be_empty }
10
+
11
+ its(:buttons) { should be_empty }
12
+ its(:fields) { should be_empty }
13
+ its(:sensors) { should be_empty }
14
+ its(:switches) { should be_empty }
15
+
16
+ describe ".build" do
17
+ it "delegates to Builder" do
18
+ block = -> {}
19
+ expect(WhippedCream::Builder).to receive(:build).with(&block)
20
+ described_class.build(&block)
21
+ end
22
+ end
23
+
24
+ describe ".from_file" do
25
+ it "delegates to Builder" do
26
+ path = "foo/bar"
27
+ expect(WhippedCream::Builder).to receive(:from_file).with(path)
28
+ described_class.from_file(path)
29
+ end
30
+ end
31
+
32
+ describe ".from_string" do
33
+ it "delegates to Builder" do
34
+ string = "name 'Garage'"
35
+ expect(WhippedCream::Builder).to receive(:from_string).with(string)
36
+ described_class.from_string(string)
37
+ end
38
+ end
39
+
40
+ context "with a button" do
41
+ before do
42
+ plugin.controls << WhippedCream::Button.new("Open/Close")
43
+ end
44
+
45
+ its(:controls) { should_not be_empty }
46
+ its(:buttons) { should_not be_empty }
47
+ end
48
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe WhippedCream::Runner do
4
+ subject(:runner) { described_class.new(plugin) }
5
+
6
+ let(:plugin) {
7
+ WhippedCream::Plugin.build do
8
+ name "Garage"
9
+ end
10
+ }
11
+
12
+ its(:name) { should eq("Garage") }
13
+
14
+ context "with a button" do
15
+ let(:plugin) {
16
+ WhippedCream::Plugin.build do
17
+ button "Open/Close", pin: 1
18
+ end
19
+ }
20
+
21
+ it "sets up that pin with direction: :out" do
22
+ pin = runner.pins[:open_close]
23
+
24
+ expect(pin).to be_a(PiPiper::Pin)
25
+ expect(pin.pin).to eq(1)
26
+ expect(pin.direction).to eq(:out)
27
+ end
28
+
29
+ it "defines an open_close method that taps the pin" do
30
+ pin = runner.pins[:open_close]
31
+
32
+ expect(pin).to receive(:on)
33
+ expect(runner).to receive(:sleep).with(0.25)
34
+ expect(pin).to receive(:off)
35
+
36
+ runner.open_close
37
+ end
38
+ end
39
+
40
+ context "with a sensor" do
41
+ let(:plugin) {
42
+ WhippedCream::Plugin.build do
43
+ sensor "Door", pin: 2, low: "Open", high: "Closed"
44
+ end
45
+ }
46
+
47
+ it "sets up that pin with direction: :in" do
48
+ pin = runner.pins[:door]
49
+
50
+ expect(pin).to be_a(PiPiper::Pin)
51
+ expect(pin.pin).to eq(2)
52
+ expect(pin.direction).to eq(:in)
53
+ end
54
+
55
+ it "defines a method that reads and converts the pin's value" do
56
+ pin = runner.pins[:door]
57
+ pin.stub value: 1
58
+
59
+ expect(runner.door).to eq("Closed")
60
+ end
61
+
62
+ context "with a block and no pin" do
63
+ let(:plugin) {
64
+ WhippedCream::Plugin.build do
65
+ sensor "Foo" do
66
+ "Bar"
67
+ end
68
+ end
69
+ }
70
+
71
+ it "does not set up a pin" do
72
+ expect(runner.pins[:door]).to be_nil
73
+ end
74
+
75
+ it "defines a method that calls the block" do
76
+ expect(runner.foo).to eq("Bar")
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe WhippedCream::Sensor do
4
+ subject(:sensor) { described_class.new(name, options, &block) }
5
+
6
+ let(:name) { "Door" }
7
+ let(:options) {
8
+ {
9
+ pin: 2,
10
+ high: "Open",
11
+ low: "Closed",
12
+ on_high: :door_opened
13
+ }
14
+ }
15
+ let(:block) { nil }
16
+
17
+ its(:name) { should eq(name) }
18
+ its(:id) { should eq(:door) }
19
+
20
+ its(:pin) { should eq(2) }
21
+
22
+ its(:high) { should eq("Open") }
23
+ its(:low) { should eq("Closed") }
24
+
25
+ its(:on_high) { should eq(:door_opened) }
26
+ its(:on_low) { should be_nil }
27
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe WhippedCream::Server do
4
+ subject(:server) { described_class.new(plugin) }
5
+
6
+ let(:plugin) {
7
+ WhippedCream::Plugin.build do
8
+ button "Open/Close", pin: 1
9
+ end
10
+ }
11
+
12
+ before do
13
+ Rack::Server.stub :start
14
+ end
15
+
16
+ it "creates a runner with the plugin" do
17
+ server.runner.stub :sleep
18
+
19
+ server.runner.open_close
20
+ end
21
+
22
+ it "reuses the same runner" do
23
+ expect(server.runner).to eq(server.runner)
24
+ end
25
+
26
+ it "builds up a Sinatra application from a plugin" do
27
+ server.start
28
+
29
+ expect(
30
+ server.web.routes['GET'].find { |route| route.first.match('/open_close') }
31
+ ).to be_true
32
+ end
33
+
34
+ it "starts the Sinatra application" do
35
+ expect(Rack::Server).to receive(:start)
36
+
37
+ server.start
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe WhippedCream do
4
+ it "should have a version number" do
5
+ expect(WhippedCream::VERSION).to_not be_empty
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ ENV['RUBY_ENV'] ||= 'test'
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
+
5
+ puts RUBY_DESCRIPTION
6
+
7
+ unless ENV['skip_coverage']
8
+ require 'simplecov'
9
+ SimpleCov.start
10
+ end
11
+
12
+ require 'whipped-cream'
13
+
14
+ RSpec.configure do |config|
15
+ config.expect_with :rspec do |c|
16
+ c.syntax = :expect
17
+ end
18
+
19
+ config.order = :random
20
+ end
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'whipped-cream/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'whipped-cream'
8
+ spec.version = WhippedCream::VERSION
9
+ spec.authors = ["Justin Campbell"]
10
+ spec.email = ["justin@justincampbell.me"]
11
+ spec.description = "HTTP topping for Raspberry Pi"
12
+ spec.summary = "HTTP topping for Raspberry Pi"
13
+ spec.homepage = 'https://github.com/justincampbell/whipped-cream'
14
+ spec.license = 'MIT'
15
+
16
+ spec.post_install_message = File.read('WELCOME')
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.required_ruby_version = '>= 1.9.3'
24
+
25
+ spec.add_runtime_dependency 'pi_piper'
26
+ spec.add_runtime_dependency 'sinatra'
27
+ spec.add_runtime_dependency 'thor'
28
+
29
+ spec.add_development_dependency 'bundler', '~> 1.3'
30
+ spec.add_development_dependency 'cane'
31
+ spec.add_development_dependency 'rake'
32
+ spec.add_development_dependency 'rspec'
33
+ spec.add_development_dependency 'simplecov'
34
+ end
metadata ADDED
@@ -0,0 +1,226 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: whipped-cream
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1pre1
5
+ prerelease: 5
6
+ platform: ruby
7
+ authors:
8
+ - Justin Campbell
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: pi_piper
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: sinatra
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: thor
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.3'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.3'
78
+ - !ruby/object:Gem::Dependency
79
+ name: cane
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rake
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: simplecov
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ description: HTTP topping for Raspberry Pi
143
+ email:
144
+ - justin@justincampbell.me
145
+ executables:
146
+ - whipped-cream
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - .cane
151
+ - .gitignore
152
+ - .rspec
153
+ - .ruby-version
154
+ - .travis.yml
155
+ - Gemfile
156
+ - Guardfile
157
+ - LICENSE.txt
158
+ - README.md
159
+ - Rakefile
160
+ - WELCOME
161
+ - bin/whipped-cream
162
+ - demo.rb
163
+ - lib/whipped-cream.rb
164
+ - lib/whipped-cream/builder.rb
165
+ - lib/whipped-cream/button.rb
166
+ - lib/whipped-cream/cli.rb
167
+ - lib/whipped-cream/control.rb
168
+ - lib/whipped-cream/pi_piper.rb
169
+ - lib/whipped-cream/plugin.rb
170
+ - lib/whipped-cream/public/assets/application.css
171
+ - lib/whipped-cream/runner.rb
172
+ - lib/whipped-cream/sensor.rb
173
+ - lib/whipped-cream/server.rb
174
+ - lib/whipped-cream/version.rb
175
+ - lib/whipped-cream/views/button.erb
176
+ - lib/whipped-cream/views/index.erb
177
+ - lib/whipped-cream/views/layout.erb
178
+ - lib/whipped-cream/views/sensor.erb
179
+ - spec/lib/whipped-cream/builder_spec.rb
180
+ - spec/lib/whipped-cream/button_spec.rb
181
+ - spec/lib/whipped-cream/cli_spec.rb
182
+ - spec/lib/whipped-cream/plugin_spec.rb
183
+ - spec/lib/whipped-cream/runner_spec.rb
184
+ - spec/lib/whipped-cream/sensor_spec.rb
185
+ - spec/lib/whipped-cream/server_spec.rb
186
+ - spec/lib/whipped-cream_spec.rb
187
+ - spec/spec_helper.rb
188
+ - whipped-cream.gemspec
189
+ homepage: https://github.com/justincampbell/whipped-cream
190
+ licenses:
191
+ - MIT
192
+ post_install_message: ! "\n Thanks for installing whipped-cream! Please don't hesitate
193
+ to report bugs\n and feature requests at:\n\n https://github.com/justincampbell/whipped-cream/issues\n\n
194
+ \ To run a demo, just do:\n\n whipped-cream demo\n\n Or use the help command
195
+ to see more options:\n\n whipped-cream help\n\n"
196
+ rdoc_options: []
197
+ require_paths:
198
+ - lib
199
+ required_ruby_version: !ruby/object:Gem::Requirement
200
+ none: false
201
+ requirements:
202
+ - - ! '>='
203
+ - !ruby/object:Gem::Version
204
+ version: 1.9.3
205
+ required_rubygems_version: !ruby/object:Gem::Requirement
206
+ none: false
207
+ requirements:
208
+ - - ! '>'
209
+ - !ruby/object:Gem::Version
210
+ version: 1.3.1
211
+ requirements: []
212
+ rubyforge_project:
213
+ rubygems_version: 1.8.23
214
+ signing_key:
215
+ specification_version: 3
216
+ summary: HTTP topping for Raspberry Pi
217
+ test_files:
218
+ - spec/lib/whipped-cream/builder_spec.rb
219
+ - spec/lib/whipped-cream/button_spec.rb
220
+ - spec/lib/whipped-cream/cli_spec.rb
221
+ - spec/lib/whipped-cream/plugin_spec.rb
222
+ - spec/lib/whipped-cream/runner_spec.rb
223
+ - spec/lib/whipped-cream/sensor_spec.rb
224
+ - spec/lib/whipped-cream/server_spec.rb
225
+ - spec/lib/whipped-cream_spec.rb
226
+ - spec/spec_helper.rb