polleverywhere 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2@polleverywhere_gem
data/Gemfile CHANGED
@@ -5,6 +5,10 @@ gemspec
5
5
 
6
6
  group :development, :test do
7
7
  gem 'rb-fsevent', :require => false if RUBY_PLATFORM =~ /darwin/i
8
- gem 'rspec'
9
8
  gem 'guard-rspec'
9
+ gem 'growl'
10
+ gem 'rspec'
11
+ gem 'haml'
12
+ gem 'rake'
13
+ gem 'ruby-debug19'
10
14
  end
data/Guardfile CHANGED
@@ -1,7 +1,7 @@
1
1
  # A sample Guardfile
2
2
  # More info at https://github.com/guard/guard#readme
3
3
 
4
- guard 'rspec', :version => 2, :cli => '--colour' do
4
+ guard 'rspec', :version => 2, :cli => '--colour --debugger' do
5
5
  watch(%r{^spec/.+_spec\.rb})
6
6
  watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
7
  watch('spec/spec_helper.rb') { "spec" }
data/README.md CHANGED
@@ -1,12 +1,44 @@
1
1
  # Poll Everywhere API Gem
2
2
 
3
- Integrate Poll Everywhere into your Ruby applications.
3
+ The Poll Everywhere gem is the best way to integrate Poll Everywhere with your applications.
4
+
5
+ # Getting Started
6
+
7
+ Install the Poll Everywhere rubygem:
8
+
9
+ sudo gem install polleverywhere
10
+
11
+ If you're using bundler, add the following line to your Gemfile:
12
+
13
+ gem "polleverywhere"
14
+
15
+ # Accessing your polls
16
+
17
+ require 'polleverywhere'
18
+
19
+ # Specify your username and password here
20
+ PollEverywhere.config do
21
+ username "my_username"
22
+ password "my_password"
23
+ end
24
+
25
+ # Now start playing!
26
+ polls = PollEverywhere::Poll.all
27
+ poll = poll.first
28
+ poll.close
29
+ poll.title = "Hey there, I like changing titles around"
30
+ poll.options = %w[uno dos tres]
31
+ poll.save
32
+ poll.open
33
+
34
+ You can do all sorts of fun stuff with polls!
4
35
 
5
36
  # TODO
37
+
6
38
  A roadmap for our API:
7
39
 
8
- * Build an app layer that we can put on top of a sync/async backend.
9
- * Build out a serialization layer for the model class (to_json,from_json,etc)
10
- * Implement specs that validate app layer
11
- * Implement specs that validate protocol/API layer
40
+ * API models and documentation for more pieces of our application
41
+ * Asyn/Evented HTTP adapter
42
+ * App layer specs
43
+ * Protocol/API layer specs
12
44
  * Implement documentation system around protocol/API layer specs so that we can run our specs againts a staging environment and validate that it works.
data/Rakefile CHANGED
@@ -1,2 +1,16 @@
1
1
  require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
+ require 'haml'
4
+ require 'polleverywhere'
5
+
6
+ namespace :doc do
7
+ desc "Generate API documentation"
8
+ task :api do
9
+ PollEverywhere.config do
10
+ username "test"
11
+ password "test"
12
+ end
13
+
14
+ puts PollEverywhere::API::Documentation.generate
15
+ end
16
+ end
data/docs/api.haml ADDED
@@ -0,0 +1,94 @@
1
+ !!! html
2
+ %html
3
+ %head
4
+ %title Poll Everywhere API
5
+ %body
6
+ %header
7
+ %h1 Poll Everywhere API Documentation
8
+ %em Disclaimer: this documentation is only intended for an internal preview. Hold off on using this stuff until we announce it on our blog.
9
+
10
+ %section
11
+ %p This API assumes that you are familar with the low-level semantics of HTTP, including the use of tools like cURL, and JSON.
12
+
13
+ %h1 Authentication
14
+ %p HTTP Basic authentication is currently used for logging into Poll Everywhere accounts.
15
+
16
+ %h1 We use JSON
17
+ %p Simple JSON key-value data structures are used throughout the API application to persist and modify data to the web application. Typically resources will include a root key that corresponds with the name of the name of the resource; from example, multiple choice polls all have the root key 'multiple_choice_poll'.
18
+
19
+ %h1 Polls
20
+ %h2 Multiple choice polls
21
+ %h3 JSON Attributes
22
+ %dl
23
+ %dt=PollEverywhere::MultipleChoicePoll.root_key
24
+ %dd Root key for multiple choice poll data.
25
+ %dd
26
+ %dl
27
+ -PollEverywhere::MultipleChoicePoll.props.each do |_, prop|
28
+ %dt=prop.name
29
+ %dd=prop.description
30
+ -if prop.name == :options
31
+ %dd
32
+ %dl
33
+ -PollEverywhere::MultipleChoicePoll::Option.props.each do |_, prop|
34
+ %dt=prop.name
35
+ %dd=prop.description
36
+
37
+ %h3 Creating a multiple choice poll
38
+ -@mcp = PollEverywhere::MultipleChoicePoll.from_hash(:title => 'Hey dude!', :options => %w[red blue green]).save
39
+ %code=examples @mcp
40
+
41
+ %h3 Changing the title of the multiple choice poll
42
+ -@mcp.title = "I like different titles"
43
+ -@mcp.save
44
+ %code=examples @mcp
45
+
46
+ %h3 Closing and opening multiple choice polls
47
+ %p To close a poll, change the state to "closed"
48
+ -@mcp.stop
49
+ %code=examples @mcp
50
+
51
+ %p To open it back up (and allow responses to come on through) change the state to "opened"
52
+ -@mcp.start
53
+ %code=examples @mcp
54
+
55
+ %h3 Delete a multiple choice poll
56
+ %p When you're totally finished with the poll and you want to tidy things up a bit, you can delete polls
57
+ -@mcp.destroy
58
+ %code=examples @mcp
59
+
60
+ %h2 Free text polls
61
+ %h3 JSON Attributes
62
+ %dl
63
+ %dt=PollEverywhere::FTP.root_key
64
+ %dd Root key for free text poll data.
65
+ %dd
66
+ %dl
67
+ -PollEverywhere::FTP.props.each do |_, prop|
68
+ %dt=prop.name
69
+ %dd=prop.description
70
+
71
+ %h3 Create free text polls
72
+ %p Creating a free text poll is similar to building a multiple choice poll, just without the options attribute.
73
+ -@ftp = PollEverywhere::FreeTextPoll.from_hash(:title => "What is the meaning of life?").save
74
+ %code=examples @ftp
75
+
76
+ %h3 Modify a free text poll
77
+ %p Change the title attribute to change the poll
78
+ -@ftp.state = "opened"
79
+ -@ftp.save
80
+ %code=examples @ftp
81
+
82
+ %h3 Delete a free text poll
83
+ -@ftp.destroy
84
+ %code=examples @ftp
85
+ %footer
86
+ %p
87
+ This documentation was automatically generated by the Poll Everywhere rubygem at
88
+ =succeed '.' do
89
+ %a(href="http://github.com/polleverywhere/polleverywhere") http://github.com/polleverywhere/polleverywhere
90
+ Community support for the API is available on the
91
+ %a(href="http://groups.google.com/group/polleverywhere-dev") Poll Everywhere Developer mailing list
92
+ and paid professional support at
93
+ =succeed '.' do
94
+ %a(href="http://www.polleverywhere.com/professional-support") Poll Everywhere
@@ -1,3 +1,24 @@
1
- module Polleverywhere
2
- # Your code goes here...
3
- end
1
+ module PollEverywhere
2
+ autoload :Configuration, 'polleverywhere/configuration'
3
+ autoload :HTTP, 'polleverywhere/http'
4
+ autoload :API, 'polleverywhere/api'
5
+ autoload :CoreExt, 'polleverywhere/core_ext'
6
+ autoload :Serializable, 'polleverywhere/serializable'
7
+ autoload :Configurable, 'polleverywhere/configurable'
8
+ autoload :Models, 'polleverywhere/models'
9
+
10
+ # Lets make life easier for developers and provide shortcuts to module and class names.
11
+ # PollEverywhere::Model::MultipleChoicePoll now becomes PollEverywhere::MCP. Cool eh? Thank me later.
12
+ MCP = MultipleChoicePoll = Models::MultipleChoicePoll
13
+ FTP = FreeTextPoll = Models::FreeTextPoll
14
+
15
+ def self.config(&block)
16
+ @config ||= Configuration.new
17
+ @config.configure(&block) if block
18
+ @config
19
+ end
20
+
21
+ def self.http
22
+ HTTP::RequestBuilder.new
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ require 'haml'
2
+
3
+ module PollEverywhere
4
+ module API
5
+ # Generates API integration documentation from HAML
6
+ class Documentation
7
+ attr_reader :template
8
+
9
+ def initialize(template)
10
+ @template = template
11
+ end
12
+
13
+ # Generates API documentation
14
+ def generate
15
+ Haml::Engine.new(template, :format => :html5).render(self)
16
+ end
17
+
18
+ # Generate the HAML documentation from the default docs path
19
+ def self.generate(path=File.expand_path('../../../docs/api.haml', __FILE__))
20
+ new(File.read(path)).generate
21
+ end
22
+
23
+ def examples(model)
24
+ model.http.adapter.request.to_curl
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ module PollEverywhere # :nodoc
2
+ # The configurable mixin is a tiny DSL that instance evals words in
3
+ # a class variable for terse configuration code.
4
+ module Configurable
5
+ def self.included(base)
6
+ base.send :extend, ClassMethods
7
+ base.send :include, InstanceMethods
8
+ end
9
+
10
+ module InstanceMethods
11
+ # Pass a block into the instance to configure the attributes on the class
12
+ def configure(&block)
13
+ instance_eval(&block)
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+ # Setup an instance method on the target class that allows the setting
19
+ # of an attribute both via Class.new.attr = "fun" and Class.new.attr "fun"
20
+ def configurable(*attrs)
21
+ attrs.each do |attr|
22
+ class_eval %{
23
+ attr_writer :#{attr}
24
+
25
+ def #{attr}(val=nil)
26
+ val ? self.#{attr} = val : @#{attr}
27
+ end}
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ require 'uri'
2
+ require 'base64'
3
+
4
+ module PollEverywhere
5
+ # Configuration information for the Poll Everywhere gem.
6
+ class Configuration
7
+ include Configurable
8
+ configurable :url, :username, :password, :http_adapter
9
+
10
+ # Setup default values for configuration class as instance variables.
11
+ def initialize
12
+ self.url = "http://api.polleverywhere.com"
13
+ self.http_adapter = :sync
14
+ end
15
+
16
+ # Make sure that a set URL is always an URI object.
17
+ def url=(val)
18
+ @url = URI.parse(val)
19
+ end
20
+
21
+ # Setup the HTTP adapter that will execute HTTP requests.
22
+ def http_adapter=(name)
23
+ @http_adapter = HTTP.adapter(name.to_sym)
24
+ end
25
+
26
+ # Builds an HTTP Basic header for our authentication. Eventually
27
+ # this form of authorization will be replaced with a token authorization
28
+ # so that a password is not required.
29
+ def http_basic_credentials
30
+ "Basic #{Base64.encode64("#{username}:#{password}")}"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,74 @@
1
+ module PollEverywhere
2
+ module CoreExt #:nodoc:
3
+
4
+ # A hash with indifferent access and magic predicates.
5
+ #
6
+ # hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
7
+ #
8
+ # hash[:foo] #=> 'bar'
9
+ # hash['foo'] #=> 'bar'
10
+ # hash.foo? #=> true
11
+ #
12
+ class HashWithIndifferentAccess < ::Hash #:nodoc:
13
+ def initialize(hash={})
14
+ super()
15
+ hash.each do |key, value|
16
+ self[convert_key(key)] = value
17
+ end
18
+ end
19
+
20
+ def [](key)
21
+ super(convert_key(key))
22
+ end
23
+
24
+ def []=(key, value)
25
+ super(convert_key(key), value)
26
+ end
27
+
28
+ def delete(key)
29
+ super(convert_key(key))
30
+ end
31
+
32
+ def values_at(*indices)
33
+ indices.collect { |key| self[convert_key(key)] }
34
+ end
35
+
36
+ def merge(other)
37
+ dup.merge!(other)
38
+ end
39
+
40
+ def merge!(other)
41
+ other.each do |key, value|
42
+ self[convert_key(key)] = value
43
+ end
44
+ self
45
+ end
46
+
47
+ protected
48
+
49
+ def convert_key(key)
50
+ key.is_a?(Symbol) ? key.to_s : key
51
+ end
52
+
53
+ # Magic predicates. For instance:
54
+ #
55
+ # options.force? # => !!options['force']
56
+ # options.shebang # => "/usr/lib/local/ruby"
57
+ # options.test_framework?(:rspec) # => options[:test_framework] == :rspec
58
+ #
59
+ def method_missing(method, *args, &block)
60
+ method = method.to_s
61
+ if method =~ /^(\w+)\?$/
62
+ if args.empty?
63
+ !!self[$1]
64
+ else
65
+ self[$1] == args.first
66
+ end
67
+ else
68
+ self[method]
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,20 @@
1
+ module PollEverywhere
2
+ module HTTP
3
+ autoload :RequestBuilder, 'polleverywhere/http/request_builder'
4
+ autoload :Adapter, 'polleverywhere/http/adapter'
5
+
6
+ # Shortcut for getting adapater instances
7
+ def self.adapter(name, &block)
8
+ Adapter.registry[name].new(&block)
9
+ end
10
+
11
+ # Simple HTTP request/response objects for our adapter and DSL
12
+ class Request < Struct.new(:method, :url, :headers, :body)
13
+ def to_curl
14
+ %(curl -X #{method.to_s.upcase} #{headers.map{|h,v| %(-H "#{h}: #{v}")}.join(" ")} -d "#{body.gsub(/[!"`']/){|m| "\\#{m}" }}" "#{url}")
15
+ end
16
+ end
17
+
18
+ Response = Struct.new(:status, :headers, :body)
19
+ end
20
+ end
@@ -0,0 +1,62 @@
1
+ require 'rest-client'
2
+
3
+ module PollEverywhere
4
+ module HTTP
5
+ # HTTP adapters make it possible to deal with syncronous or asyncronous HTTP libraries
6
+ module Adapter
7
+ def self.registry
8
+ @adapters ||= Hash.new{|hash,key| h[k.to_sym] }
9
+ end
10
+
11
+ def self.register(name, klass)
12
+ registry[name] = klass
13
+ end
14
+
15
+ class Sync
16
+ attr_accessor :request, :response
17
+
18
+ def initialize
19
+ yield self if block_given?
20
+ end
21
+
22
+ def execute(request, &block)
23
+ @request = request
24
+ # TODO get rid of the dependency for RestClient by sucking it up and using the Ruby HTTP client
25
+ RestClient::Request.execute(:method => request.method, :url => request.url.to_s, :payload => request.body, :headers => request.headers) do |r,_,_,_|
26
+ @response = block.call Response.new(r.code, r.headers, r.body)
27
+ end
28
+ end
29
+ end
30
+
31
+ class Doc < Sync
32
+ def execute(request, &block)
33
+ requests << request
34
+ super request, &block
35
+ end
36
+
37
+ def requests
38
+ @requests ||= []
39
+ end
40
+
41
+ def last_requests
42
+ requests[(@last_request ||= 0)..(@last_request=requests.size)]
43
+ end
44
+ end
45
+
46
+ class Test
47
+ def execute(request, &block)
48
+ requests << request
49
+ block.call(Response.new)
50
+ end
51
+
52
+ def requests
53
+ @requests ||= []
54
+ end
55
+ end
56
+
57
+ self.register :sync, Sync
58
+ self.register :doc, Doc
59
+ self.register :test, Test
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,78 @@
1
+ require 'uri'
2
+ require 'base64'
3
+
4
+ module PollEverywhere
5
+ module HTTP # :nodoc
6
+ # DSL for building requests within our application that build a Request object and send them to
7
+ # an adapter to fulfill the request.
8
+ class RequestBuilder
9
+ attr_accessor :method, :body, :headers, :params, :url, :format, :response, :adapter, :base_url
10
+
11
+ def initialize(adapter=PollEverywhere.config.http_adapter, base_url=PollEverywhere.config.url)
12
+ self.adapter = adapter
13
+ self.base_url = base_url
14
+ self.headers = {
15
+ 'Content-Type' => 'application/json',
16
+ 'Accept' => 'application/json',
17
+ 'Authorization' => PollEverywhere.config.http_basic_credentials
18
+ }
19
+
20
+ yield self if block_given?
21
+ end
22
+
23
+ def put(body=nil)
24
+ self.method, self.body = :put, body
25
+ self
26
+ end
27
+
28
+ def post(body=nil)
29
+ self.method, self.body = :post, body
30
+ self
31
+ end
32
+
33
+ def get(params={})
34
+ self.method, self.params = :get, params
35
+ self
36
+ end
37
+
38
+ def delete(params={})
39
+ self.method, self.params = :delete, params
40
+ self
41
+ end
42
+
43
+ # Specify the URL of the request.
44
+ def to(url)
45
+ self.url = url
46
+ self
47
+ end
48
+ alias :from :to
49
+
50
+ # Specify the mime-type/format of the request.
51
+ def as(format)
52
+ self.format = format
53
+ self
54
+ end
55
+
56
+ def request
57
+ Request.new(method, url_for(url), headers, body)
58
+ end
59
+
60
+ # Generate a Request object, send it to an adapter, and have it fullfill the response by
61
+ # yielding a Response object.
62
+ def response(&block)
63
+ adapter.execute request, &block
64
+ end
65
+
66
+ private
67
+ # If a base_url is given, url_for can detect if a given .to is a path or URL, then
68
+ # setup the approriate URL for the request.
69
+ def url_for(uri)
70
+ if uri.to_s !~ /^http/ and base_url
71
+ "#{base_url}#{uri}"
72
+ else
73
+ uri.to_s
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,166 @@
1
+ module PollEverywhere # :nodoc
2
+ module Models # :nodoc
3
+
4
+ # Poll is an abstract base classe for multiple choice and free text polls
5
+ class Poll
6
+ include Serializable
7
+
8
+ prop :id
9
+
10
+ prop :updated_at do
11
+ description %{The date and time the poll was last updated.}
12
+ end
13
+
14
+ prop :title do
15
+ description %{Name of the poll. The title is visible when viewing charts, tables, and other views.}
16
+ end
17
+
18
+ prop :opened_at do
19
+ description %{Data and time that the poll was started.}
20
+ end
21
+
22
+ prop :options do
23
+ description %{The possible choices that people choose for a poll.}
24
+ end
25
+
26
+ prop :permalink do
27
+ description %{An obscufated ID that's used as a private link for sharing the poll}
28
+ end
29
+
30
+ prop :state do
31
+ description %{Determines whether or not a poll can recieve responses. If the state is 'closed', the poll won't receive responses from the audience. If the poll is 'opened', the poll can receive responses from the audience. If the state is 'maxed_out', the poll won't receive responses until the account is upgraded to support more poll responses.}
32
+ end
33
+
34
+ attr_accessor :http
35
+
36
+ def initialize(http=PollEverywhere.http)
37
+ self.http = http
38
+ end
39
+
40
+ # Start the poll so that it may receive audience responses
41
+ def start
42
+ state = "opened"
43
+ save
44
+ end
45
+
46
+ # Stop the poll so that it stops receieving responses
47
+ def stop
48
+ state = "closed"
49
+ save
50
+ end
51
+
52
+ def persisted?
53
+ !!permalink
54
+ end
55
+
56
+ def save
57
+ if persisted?
58
+ http.put(to_json).as(:json).to(path).response do |response|
59
+ from_json response.body
60
+ end
61
+ else
62
+ http.post(to_json).as(:json).to('/multiple_choice_polls').response do |response|
63
+ from_json response.body
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.get(permalink)
69
+ from_hash(:permalink => permalink).fetch
70
+ end
71
+
72
+ def self.all
73
+ PollEverywhere.http.get.from("/my/polls").as(:json).response do |response|
74
+ ::JSON.parse(response.body).map do |hash|
75
+ case hash.keys.first.to_sym
76
+ when MCP.root_key
77
+ MCP.from_hash(hash)
78
+ when FTP.root_key
79
+ MCP.from_hash(hash)
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def destroy
86
+ http.delete(path).response do |response|
87
+ self.id = self.permalink = nil
88
+ end
89
+ end
90
+
91
+ def path
92
+ "/#{self.class.root_key}s/#{permalink}" if persisted?
93
+ end
94
+
95
+ def possible_states
96
+ [:opened, :closed, :maxed_out]
97
+ end
98
+ end
99
+
100
+ class FreeTextPoll < Poll
101
+ root_key :free_text_poll
102
+
103
+ prop :keyword do
104
+ description "The keyword that's used to submit a response to the poll from participants that use SMS."
105
+ end
106
+ end
107
+
108
+ class MultipleChoicePoll < Poll
109
+
110
+ root_key :multiple_choice_poll
111
+
112
+ class Option
113
+ include Serializable
114
+
115
+ prop :id
116
+
117
+ prop :value do
118
+ description "Text that is displayed in the chart that represents what a participant chooses when they response to a poll."
119
+ end
120
+
121
+ prop :keyword do
122
+ description "The keyword that's used to submit a response to the poll from participants that use SMS."
123
+ end
124
+
125
+ attr_reader :poll
126
+
127
+ def initialize(poll)
128
+ @poll = poll
129
+ end
130
+ end
131
+
132
+ # Choices for a multiple choice poll
133
+ def options
134
+ @options ||= []
135
+ end
136
+
137
+ # Accept an array of options as strings, hashes, or options objects.
138
+ def options=(options)
139
+ @options = options.map do |val|
140
+ case val
141
+ when Option
142
+ val.poll = self
143
+ val
144
+ when Hash
145
+ Option.new(self).from_hash(val)
146
+ else
147
+ Option.new(self).from_hash(:value => val.to_s)
148
+ end
149
+ end
150
+ end
151
+
152
+ def fetch
153
+ http.get.from(path).as(:json).response do |response|
154
+ from_json response.body
155
+ end
156
+ end
157
+
158
+ # Add the serialize options hash to the meix
159
+ def to_hash
160
+ hash = super
161
+ hash[:multiple_choice_poll][:options] = options.map(&:to_hash)
162
+ hash
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,127 @@
1
+ require 'json'
2
+
3
+ module PollEverywhere
4
+ module Serializable
5
+ def self.included(base)
6
+ base.send :extend, ClassMethods
7
+ base.send :include, InstanceMethods
8
+ end
9
+
10
+ # Property is somewhat of a metaclass that stores validations, name, etc.
11
+ # for the fields that belond to a serializable model. Since a Property lives
12
+ # at the class level, we have a Value class that
13
+ class Property
14
+ include Configurable
15
+
16
+ attr_accessor :name, :validations
17
+
18
+ configurable :description
19
+
20
+ def initialize(name, configuration={})
21
+ self.name = name.to_sym
22
+ # Set attributes on the class from a given hash
23
+ configuration.each do |attr, args|
24
+ self.send("#{attr}=", *args) if self.respond_to? "#{attr}="
25
+ end
26
+ end
27
+
28
+ def value
29
+ Value.new(self)
30
+ end
31
+
32
+ # Contains the value of the property, runs validations, and
33
+ # tracks property changes
34
+ class Value
35
+ attr_reader :property, :original
36
+ attr_accessor :current
37
+
38
+ def initialize(property)
39
+ @property = property
40
+ end
41
+ end
42
+ end
43
+
44
+ module ClassMethods
45
+ # Set or get the root key of the model
46
+ def root_key(name=nil)
47
+ name ? @name = name.to_sym : @name
48
+ end
49
+
50
+ # Define a property at the class level
51
+ def prop(name, &block)
52
+ prop = props[name]
53
+ prop.instance_eval(&block) if block
54
+ prop
55
+ end
56
+
57
+ # Setup attributes hash and delegate setters/getters to the base class.
58
+ def props
59
+ @props ||= Hash.new do |props,name|
60
+ # Define the methods at the instance level
61
+ class_eval %{
62
+ def #{name}=(val)
63
+ props[:#{name}].current = val
64
+ end
65
+
66
+ def #{name}
67
+ props[:#{name}].current
68
+ end}
69
+
70
+ # Setup the attribute class
71
+ props[name] = Property.new(name)
72
+
73
+ end.merge superclass_props
74
+ end
75
+
76
+ # Instanciate a class from a hash
77
+ def from_hash(hash)
78
+ new.from_hash(hash)
79
+ end
80
+
81
+ protected
82
+ # Grab attributes from the superclass, clone them, and then we can modify them locally.
83
+ def superclass_props
84
+ (superclass.respond_to?(:props) && superclass.props.clone) || {}
85
+ end
86
+ end
87
+
88
+ module InstanceMethods
89
+ def props
90
+ @props ||= self.class.props.inject CoreExt::HashWithIndifferentAccess.new do |hash, (name, property)|
91
+ hash[property.name] = property.value
92
+ hash
93
+ end
94
+ end
95
+
96
+ def to_hash
97
+ hash = props.inject CoreExt::HashWithIndifferentAccess.new do |hash, (name, value)|
98
+ hash[name] = self.send(name)
99
+ hash
100
+ end
101
+ # Add a root key if one is specified
102
+ self.class.root_key ? { self.class.root_key => hash } : hash
103
+ end
104
+
105
+ # Set the attributes from a hash
106
+ def from_hash(hash)
107
+ hash = CoreExt::HashWithIndifferentAccess.new(hash)
108
+ # Pop off the root key if one is specified and given
109
+ self.class.root_key and hash[self.class.root_key] and hash = hash[self.class.root_key]
110
+ # Then set the attributes on the klass if they're present
111
+ hash.each do |name, value|
112
+ self.send("#{name}=", value) if self.respond_to? name
113
+ end
114
+ self
115
+ end
116
+
117
+ def to_json
118
+ JSON.pretty_generate(to_hash)
119
+ end
120
+
121
+ def from_json(json)
122
+ from_hash JSON.parse(json)
123
+ end
124
+ end
125
+ end
126
+
127
+ end
@@ -1,3 +1,3 @@
1
1
  module Polleverywhere
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -13,9 +13,11 @@ Gem::Specification.new do |s|
13
13
  s.description = %q{An easy way to integrate Poll Everywhere into your Ruby applications.}
14
14
 
15
15
  s.rubyforge_project = "polleverywhere"
16
+ s.add_dependency "json"
17
+ s.add_development_dependency "rest-client", "~> 1.6.3"
16
18
 
17
19
  s.files = `git ls-files`.split("\n")
18
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
22
  s.require_paths = ["lib"]
21
- end
23
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe "API" do
4
+ context "polls" do
5
+ it "should get collection from my poll" do
6
+ PollEverywhere::Models::Poll.all.should have_at_least(1).items
7
+ end
8
+
9
+ context "multiple choice" do
10
+ before(:all) do
11
+ @mcp = PollEverywhere::MultipleChoicePoll.from_hash(:title => 'Hey dude!', :options => %w[red blue green])
12
+ end
13
+
14
+ context "creation" do
15
+ before(:all) do
16
+ @mcp.save
17
+ end
18
+
19
+ it "should have id" do
20
+ @mcp.id.should_not be_nil
21
+ end
22
+
23
+ it "should have permalink" do
24
+ @mcp.permalink.should_not be_nil
25
+ end
26
+
27
+ context "options" do
28
+ it "should have ids" do
29
+ @mcp.options.each do |o|
30
+ o.id.should_not be_nil
31
+ end
32
+ end
33
+
34
+ it "should have values" do
35
+ @mcp.options.each do |o|
36
+ o.value.should_not be_nil
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ context "get" do
43
+ it "should get poll" do
44
+ @gotten_mcp = PollEverywhere::MultipleChoicePoll.get(@mcp.permalink)
45
+ @gotten_mcp.title.should eql(@mcp.title)
46
+ end
47
+ end
48
+
49
+ context "updates" do
50
+ before(:all) do
51
+ @mcp.title = "My pita bread is moldy"
52
+ @mcp.options.first.value = "MOLD SUCKS!"
53
+ @mcp.save
54
+ end
55
+
56
+ it "should update options" do
57
+ @mcp.options.first.value.should eql("MOLD SUCKS!")
58
+ end
59
+
60
+ it "should update title" do
61
+ @mcp.title.should eql("My pita bread is moldy")
62
+ end
63
+ end
64
+
65
+ it "should destroy" do
66
+ @mcp.destroy
67
+ @mcp.id.should be_nil
68
+ end
69
+ end
70
+
71
+ end
72
+ end
@@ -1,5 +1,102 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Poll Everywhere" do
4
- it "shall be an awesome gem"
3
+ describe "Configuration" do
4
+ before(:all) do
5
+ @config = PollEverywhere::Configuration.new
6
+ end
7
+
8
+ %w[username password].each do |attr|
9
+ it "should have '#{attr}' configurable attribute" do
10
+ @config.send(attr, "test_val")
11
+ @config.send(attr).should eql("test_val")
12
+ end
13
+ end
14
+
15
+ context "url" do
16
+ it "should default to 'http://api.polleverywhere.com'" do
17
+ @config.url.to_s.should eql("http://api.polleverywhere.com")
18
+ end
19
+
20
+ it "should have URI instance for url configurable attribute" do
21
+ @config.url = "http://loser.com"
22
+ @config.url.to_s.should eql("http://loser.com")
23
+ @config.url.should be_instance_of(URI::HTTP)
24
+ end
25
+ end
26
+
27
+ it "should have a root configuration class" do
28
+ PollEverywhere.config.should be_instance_of(PollEverywhere::Configuration)
29
+ end
30
+ end
31
+
32
+ describe "documentation" do
33
+ it "should show curl format" do
34
+ poll = PollEverywhere::MultipleChoicePoll.new
35
+ poll.title = 'cool'
36
+ poll.options = %w[1 2 3]
37
+ poll.save
38
+ end
39
+ end
40
+
41
+ describe "Multiple Choice Poll" do
42
+ context "serialize" do
43
+ before(:all) do
44
+ @hash = {:title => 'Hey dude!', :options => %w[red blue green]}
45
+ @poll = PollEverywhere::MultipleChoicePoll.from_hash(@hash)
46
+ end
47
+
48
+ context "serialization" do
49
+ it "should serialize options" do
50
+ @hash[:options].each do |value|
51
+ @poll.to_hash[:multiple_choice_poll][:options].find{|o| o[:value] == value}.should_not be_nil
52
+ end
53
+ end
54
+ end
55
+
56
+ context "deserialization" do
57
+ it "should have options" do
58
+ @hash[:options].each do |value|
59
+ @poll.options.find{|o| o.value == value }
60
+ end
61
+ end
62
+
63
+ it "should set title" do
64
+ @poll.title.should eql(@hash[:title])
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "http" do
71
+ context "request builder" do
72
+
73
+ # it "should post" do
74
+ # PollEverywhere::HTTP::RequestBuilder.new.post('some data').to('/this/url').response do |response|
75
+ # response.body.should eql('hello my pretty')
76
+ # response.status.should eql(200)
77
+ # end
78
+ # end
79
+
80
+ # it "should put" do
81
+ # PollEverywhere::HTTP::RequestBuilder.new.put('some data').to('/this/url').response do |response|
82
+ # response.body.should eql('hello my pretty')
83
+ # response.status.should eql(200)
84
+ # end
85
+ # end
86
+
87
+ # it "should delete" do
88
+ # PollEverywhere::HTTP::RequestBuilder.new.delete.from('/this/url').response do |response|
89
+ # response.body.should eql('hello my pretty')
90
+ # response.status.should eql(200)
91
+ # end
92
+ # end
93
+
94
+ # it "should get" do
95
+ # PollEverywhere::HTTP::RequestBuilder.new.get(:page => 1).from('/this/url').response do |response|
96
+ # response.body.should eql('hello my pretty')
97
+ # response.status.should eql(200)
98
+ # end
99
+ # end
100
+
101
+ end
5
102
  end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,11 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
- require 'rspec'
3
+ require 'rspec'
4
+ require 'polleverywhere'
5
+
6
+ PollEverywhere.config do
7
+ username "test"
8
+ password "test"
9
+ url "http://localhost:3000"
10
+ http_adapter :sync
11
+ end
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polleverywhere
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
5
4
  prerelease:
6
- segments:
7
- - 0
8
- - 0
9
- - 1
10
- version: 0.0.1
5
+ version: 0.0.2
11
6
  platform: ruby
12
7
  authors:
13
8
  - Brad Gessler, Steel Fu
@@ -15,10 +10,31 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-05-24 00:00:00 -07:00
13
+ date: 2011-06-24 00:00:00 -07:00
19
14
  default_executable:
20
- dependencies: []
21
-
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: json
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: rest-client
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: 1.6.3
36
+ type: :development
37
+ version_requirements: *id002
22
38
  description: An easy way to integrate Poll Everywhere into your Ruby applications.
23
39
  email:
24
40
  - geeks@polleverywhere.com
@@ -30,13 +46,25 @@ extra_rdoc_files: []
30
46
 
31
47
  files:
32
48
  - .gitignore
49
+ - .rvmrc
33
50
  - Gemfile
34
51
  - Guardfile
35
52
  - README.md
36
53
  - Rakefile
54
+ - docs/api.haml
37
55
  - lib/polleverywhere.rb
56
+ - lib/polleverywhere/api.rb
57
+ - lib/polleverywhere/configurable.rb
58
+ - lib/polleverywhere/configuration.rb
59
+ - lib/polleverywhere/core_ext.rb
60
+ - lib/polleverywhere/http.rb
61
+ - lib/polleverywhere/http/adapter.rb
62
+ - lib/polleverywhere/http/request_builder.rb
63
+ - lib/polleverywhere/models.rb
64
+ - lib/polleverywhere/serializable.rb
38
65
  - lib/polleverywhere/version.rb
39
66
  - polleverywhere.gemspec
67
+ - spec/integration/api_spec.rb
40
68
  - spec/lib/polleverywhere_spec.rb
41
69
  - spec/spec_helper.rb
42
70
  has_rdoc: true
@@ -53,26 +81,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
53
81
  requirements:
54
82
  - - ">="
55
83
  - !ruby/object:Gem::Version
56
- hash: 3
57
- segments:
58
- - 0
59
84
  version: "0"
60
85
  required_rubygems_version: !ruby/object:Gem::Requirement
61
86
  none: false
62
87
  requirements:
63
88
  - - ">="
64
89
  - !ruby/object:Gem::Version
65
- hash: 3
66
- segments:
67
- - 0
68
90
  version: "0"
69
91
  requirements: []
70
92
 
71
93
  rubyforge_project: polleverywhere
72
- rubygems_version: 1.4.2
94
+ rubygems_version: 1.5.2
73
95
  signing_key:
74
96
  specification_version: 3
75
97
  summary: Integration Poll Everywhere into your Ruby applications
76
98
  test_files:
99
+ - spec/integration/api_spec.rb
77
100
  - spec/lib/polleverywhere_spec.rb
78
101
  - spec/spec_helper.rb