polleverywhere 0.0.6 → 0.0.7

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.
@@ -26,13 +26,13 @@
26
26
  %dd Root key for multiple choice poll data.
27
27
  %dd
28
28
  %dl
29
- -PollEverywhere::MultipleChoicePoll.props.each do |_, prop|
29
+ -PollEverywhere::MultipleChoicePoll.prop_set.each do |_, prop|
30
30
  %dt=prop.name
31
31
  %dd=prop.description
32
32
  -if prop.name == :options
33
33
  %dd
34
34
  %dl
35
- -PollEverywhere::MultipleChoicePoll::Option.props.each do |_, prop|
35
+ -PollEverywhere::MultipleChoicePoll::Option.prop_set.each do |_, prop|
36
36
  %dt=prop.name
37
37
  %dd=prop.description
38
38
 
@@ -68,7 +68,7 @@
68
68
  %dd Root key for free text poll data.
69
69
  %dd
70
70
  %dl
71
- -PollEverywhere::FTP.props.each do |_, prop|
71
+ -PollEverywhere::FTP.prop_set.each do |_, prop|
72
72
  %dt=prop.name
73
73
  %dd=prop.description
74
74
 
@@ -96,7 +96,7 @@
96
96
  %dd Root key for participant data.
97
97
  %dd
98
98
  %dl
99
- -PollEverywhere::Participant.props.each do |_, prop|
99
+ -PollEverywhere::Participant.prop_set.each do |_, prop|
100
100
  %dt=prop.name
101
101
  %dd=prop.description
102
102
 
@@ -104,12 +104,18 @@
104
104
  %p Specify the details of a new participant
105
105
  :example
106
106
  @participant = PollEverywhere::Participant.from_hash(:email => 'b.obama@whitehouse.gov', :first_name => 'Barack', :last_name => 'Obama', :password => 'mickeymouse', :responding_as => 'Barack').save
107
-
107
+
108
108
  %h2 Updating participant details
109
109
  :example
110
- @participant.first_name = "President"
110
+ @participant.responding_as = "Mr. President"
111
111
  @participant.save
112
112
 
113
+ %h2 Changing an email address
114
+ %p.http Changing an email address is a slightly special case at an HTTP level because the new email address should be in the body of the participant data and should be <code>PUT</code> to the original email address.
115
+ :example
116
+ @participant.email = "mr.president@hotmail.com"
117
+ @participant.save
118
+
113
119
  %h2 Delete a Participant
114
120
  :example
115
121
  @participant.destroy
@@ -23,6 +23,13 @@ pre
23
23
  &:hover
24
24
  overflow: visible
25
25
 
26
+ code
27
+ font-family: monaco, courier
28
+ font-size: $code-font-size
29
+ background: #eee
30
+ line-height: $base-font-size
31
+ padding: 1px
32
+
26
33
  h1,h2,h3,h4,h5,h6
27
34
  font-weight: normal
28
35
  h1
@@ -11,7 +11,14 @@ module PollEverywhere
11
11
  # Simple HTTP request/response objects for our adapter and DSL
12
12
  class Request < Struct.new(:method, :url, :headers, :body)
13
13
  def to_curl
14
- %(curl -X #{method.to_s.upcase} #{headers.map{|h,v| %(-H "#{h}: #{v}")}.join(" ")} -d "#{body.gsub(/[!"`'\n]/){|m| "\\#{m}" }}" "#{url}")
14
+ case method.to_s
15
+ when /^put|post$/i
16
+ %(curl -X #{method.to_s.upcase} #{headers.map{|h,v| %(-H "#{h}: #{v}")}.join(" ")} -d "#{body.gsub(/[!"`'\n]/){|m| "\\#{m}" }}" "#{url}")
17
+ when /^get$/i
18
+ %(curl #{headers.map{|h,v| %(-H "#{h}: #{v}")}.join(" ")} "#{url}")
19
+ else
20
+ %(curl -X #{method.to_s.upcase} #{headers.map{|h,v| %(-H "#{h}: #{v}")}.join(" ")} "#{url}")
21
+ end
15
22
  end
16
23
  end
17
24
 
@@ -20,7 +20,7 @@ module PollEverywhere
20
20
  end
21
21
 
22
22
  def execute(request, &block)
23
- @request = request
23
+ @request, @response = request, nil
24
24
  # TODO get rid of the dependency for RestClient by sucking it up and using the Ruby HTTP client
25
25
  RestClient::Request.execute(:method => request.method, :url => request.url.to_s, :payload => request.body, :headers => request.headers) do |r,_,_,_|
26
26
  @response = block.call Response.new(r.code, r.headers, r.body)
@@ -5,11 +5,17 @@ module PollEverywhere # :nodoc
5
5
 
6
6
  root_key :user
7
7
 
8
- prop :first_name
8
+ prop :first_name do
9
+ description "First name of the participant"
10
+ end
9
11
 
10
- prop :last_name
12
+ prop :last_name do
13
+ description "Last name of the participant"
14
+ end
11
15
 
12
- prop :email
16
+ prop :email do
17
+ description "Email address of pariticpant"
18
+ end
13
19
 
14
20
  prop :phone_number do
15
21
  description %{Phone number associated with the participant.}
@@ -19,7 +25,9 @@ module PollEverywhere # :nodoc
19
25
  description %{This is used to identify the participant in reports.}
20
26
  end
21
27
 
22
- prop :password
28
+ prop :password do
29
+ description %{Password that the participant may use to login to their account and view their response history.}
30
+ end
23
31
 
24
32
  attr_accessor :http
25
33
 
@@ -29,6 +37,7 @@ module PollEverywhere # :nodoc
29
37
 
30
38
  def save
31
39
  http.put(to_json).as(:json).to(path).response do |response|
40
+ value_set.commit
32
41
  from_json response.body
33
42
  end
34
43
  end
@@ -38,12 +47,12 @@ module PollEverywhere # :nodoc
38
47
  end
39
48
 
40
49
  def destroy
41
- http.delete(path).response do |response|
50
+ http.delete.from(path).response do |response|
42
51
  end
43
52
  end
44
53
 
45
54
  def path
46
- "/participants/#{email}"
55
+ "/participants/#{prop(:email).was || prop(:email).is}"
47
56
  end
48
57
 
49
58
  def fetch
@@ -11,6 +11,22 @@ module PollEverywhere
11
11
  # for the fields that belond to a serializable model. Since a Property lives
12
12
  # at the class level, we have a Value class that
13
13
  class Property
14
+ # Manage a set of properties and easily create instances of values from them.
15
+ class Set < CoreExt::HashWithIndifferentAccess
16
+ # Create a set of of properties that we can use to create instances of value sets
17
+ def initialize
18
+ super() do |props, name|
19
+ props[name] = Property.new(name)
20
+ end
21
+ end
22
+
23
+ # Return a collection fo values wrapped inside of a valuset. Valuesets make
24
+ # it easier to query and validate a collection of values.
25
+ def value_set(hash={})
26
+ Value::Set.new(*values.map(&:value))
27
+ end
28
+ end
29
+
14
30
  include Configurable
15
31
 
16
32
  attr_accessor :name, :validations
@@ -32,45 +48,109 @@ module PollEverywhere
32
48
  # Contains the value of the property, runs validations, and
33
49
  # tracks property changes
34
50
  class Value
51
+ # Manage a collection of values so that you can validate, commit, detect changes, etc in bulk
52
+ class Set < CoreExt::HashWithIndifferentAccess
53
+ def initialize(*values, &block)
54
+ # Copy the values and property names into the hash
55
+ values.each do |value|
56
+ props[value.property.name] = value
57
+ end
58
+ end
59
+
60
+ # Set the current value so that we can track dirty changes
61
+ def []=(prop_name, value)
62
+ prop(prop_name).current = value
63
+ end
64
+
65
+ # Access the current value
66
+ def [](prop_name)
67
+ prop(prop_name).current
68
+ end
69
+
70
+ # Returns a hash with a {:attr => ['original', 'changed']}.
71
+ def changes
72
+ props.inject(CoreExt::HashWithIndifferentAccess.new) do |hash, (prop_name, value)|
73
+ hash[prop_name] = value.changes if value.changed?
74
+ hash
75
+ end
76
+ end
77
+
78
+ # Detects if all properties have been changed
79
+ def changed?
80
+ props.values.any?(&:changed?)
81
+ end
82
+
83
+ # Commit changes
84
+ def commit
85
+ props.values.each(&:commit)
86
+ end
87
+
88
+ # Our collection of property values... I call them props under the assumption that they're returning values.
89
+ def props
90
+ @props ||= CoreExt::HashWithIndifferentAccess.new do |hash, key|
91
+ hash[key] = Property.new(key).value
92
+ end
93
+ end
94
+
95
+ # Prettier way to get at a prop so it doesn't feel so hashy
96
+ def prop(prop_name)
97
+ props[prop_name]
98
+ end
99
+ end
100
+
35
101
  attr_reader :property, :original
36
102
  attr_accessor :current
103
+ # Make our value DSL prettier so we can ask, value.was and value.is
104
+ alias :is :current
105
+ alias :was :original
37
106
 
38
107
  def initialize(property)
39
108
  @property = property
40
109
  end
110
+
111
+ # Detect if the values have changed since we last updated them
112
+ def changed?
113
+ original != current
114
+ end
115
+
116
+ # The original and current state of the value if it changed.
117
+ def changes
118
+ [original, current] if changed?
119
+ end
120
+
121
+ # Commit the values if they're valid
122
+ def commit
123
+ @original = @current
124
+ end
41
125
  end
42
126
  end
43
127
 
44
128
  module ClassMethods
45
129
  # Set or get the root key of the model
46
130
  def root_key(name=nil)
47
- name ? @name = name.to_sym : @name
131
+ name ? @root_key = name.to_sym : @root_key
48
132
  end
49
133
 
50
134
  # Define a property at the class level
51
135
  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)
136
+ # Setup instance methods on the class that give us a short cut to the prop values
137
+ class_eval %{
138
+ def #{name}=(val)
139
+ value_set[:#{name}] = val
140
+ end
141
+
142
+ def #{name}
143
+ value_set[:#{name}]
144
+ end}
145
+
146
+ prop_set[name].instance_eval(&block) if block
147
+ # Return the property we just created so we can chain calls
148
+ prop_set[name]
149
+ end
72
150
 
73
- end.merge superclass_props
151
+ # A property set that inherits superclass property sets.
152
+ def prop_set
153
+ @prop_set ||= Property::Set.new.merge superclass_prop_set
74
154
  end
75
155
 
76
156
  # Instanciate a class from a hash
@@ -80,21 +160,29 @@ module PollEverywhere
80
160
 
81
161
  protected
82
162
  # 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) || {}
163
+ def superclass_prop_set
164
+ (superclass.respond_to?(:prop_set) && superclass.prop_set.clone) || Property::Set.new
85
165
  end
86
166
  end
87
167
 
88
168
  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
169
+ # A value set instance for this class instance
170
+ def value_set
171
+ @value_set ||= self.class.prop_set.value_set
172
+ end
173
+
174
+ # Returns a value from the value_set for a property
175
+ def prop(name)
176
+ value_set.prop(name)
177
+ end
178
+
179
+ # Figure out if the model changed.
180
+ def changed?
181
+ value_set.changed?
94
182
  end
95
183
 
96
184
  def to_hash
97
- hash = props.inject CoreExt::HashWithIndifferentAccess.new do |hash, (name, value)|
185
+ hash = value_set.props.inject CoreExt::HashWithIndifferentAccess.new do |hash, (name, value)|
98
186
  hash[name] = self.send(name)
99
187
  hash
100
188
  end
@@ -102,6 +190,13 @@ module PollEverywhere
102
190
  self.class.root_key ? { self.class.root_key => hash } : hash
103
191
  end
104
192
 
193
+ # Debug friendly way to output model state
194
+ def to_s
195
+ %(#{self.class.name}(#{value_set.props.map{|name, val|
196
+ "#{name}:#{'`' if val.changed? }#{val.current.inspect}"
197
+ }.join(', ')}))
198
+ end
199
+
105
200
  # Set the attributes from a hash
106
201
  def from_hash(hash)
107
202
  hash = CoreExt::HashWithIndifferentAccess.new(hash)
@@ -1,3 +1,3 @@
1
1
  module PollEverywhere
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe PollEverywhere::Models::Participant do
4
+ before(:all) do
5
+ @p = PollEverywhere::Models::Participant.from_hash(:email => 'brad@brad.com')
6
+ end
7
+
8
+ context "email path" do
9
+ it "should have current email if not commited" do
10
+ @p.path.should eql('/participants/brad@brad.com')
11
+ end
12
+
13
+ it "should have old email if committed" do
14
+ @p.save
15
+ @p.email = 'super.duper@something.com'
16
+ @p.path.should eql('/participants/brad@brad.com')
17
+ end
18
+
19
+ it "should change email with old email in path" do
20
+ @p.save
21
+ @p.email = 'i.like.emails@something.com'
22
+ @p.path.should eql('/participants/super.duper@something.com')
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+
3
+ describe PollEverywhere::Serializable do
4
+ class Animal
5
+ include PollEverywhere::Serializable
6
+
7
+ prop :color
8
+ prop :smell
9
+ prop :name
10
+ end
11
+
12
+ class Furry < Animal
13
+ prop :fuzzy
14
+ end
15
+
16
+ before(:all) do
17
+ @bear = Furry.new.from_hash({
18
+ :fuzzy => true,
19
+ :name => 'Bear',
20
+ :small => 'awful',
21
+ :color => 'black'
22
+ })
23
+ end
24
+
25
+ context "property inheritence" do
26
+ it "should have subclass property" do
27
+ @bear.fuzzy.should be_true
28
+ end
29
+
30
+ it "should have superclass property" do
31
+ @bear.color.should eql('black')
32
+ end
33
+ end
34
+
35
+ context "property changes" do
36
+ it "should be changed" do
37
+ @bear.color = 'brown'
38
+ @bear.should be_changed
39
+ end
40
+ end
41
+
42
+ context "commit" do
43
+ before(:all) do
44
+ @bear.color = "red"
45
+ @bear.value_set.commit
46
+ end
47
+
48
+ it "should not be changed after commit" do
49
+ @bear.should_not be_changed
50
+ end
51
+
52
+ it "should have set current value as old value" do
53
+ @bear.prop(:color).was.should eql('red')
54
+ end
55
+
56
+ it "should have current value" do
57
+ @bear.prop(:color).is.should eql('red')
58
+ end
59
+
60
+ it "should not modify the old value" do
61
+ @bear.color = "green"
62
+ @bear.prop('color').was.should eql('red')
63
+ @bear.prop('color').is.should eql('green')
64
+ end
65
+ end
66
+ end
67
+
68
+ describe PollEverywhere::Serializable::Property do
69
+ before(:all) do
70
+ @prop = PollEverywhere::Serializable::Property.new(:field)
71
+ end
72
+
73
+ context "value" do
74
+ before(:each) do
75
+ @value = @prop.value
76
+ end
77
+
78
+ it "should detect changes" do
79
+ @value.current = "super fun times"
80
+ @value.should be_changed
81
+ end
82
+
83
+ it "should maintain original value" do
84
+ @value.current = "flimtastical"
85
+ @value.original.should be_nil
86
+ end
87
+
88
+ context "after commit" do
89
+ before(:each) do
90
+ @value.current = "dog treats"
91
+ @value.commit
92
+ end
93
+
94
+ it "should not be changed" do
95
+ @value.should_not be_changed
96
+ end
97
+
98
+ it "should have original value" do
99
+ @value.current.should eql("dog treats")
100
+ end
101
+
102
+ it "should have current value" do
103
+ @value.current.should eql("dog treats")
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ describe PollEverywhere::Serializable::Property::Set do
110
+ before(:all) do
111
+ @propset = PollEverywhere::Serializable::Property::Set.new
112
+ end
113
+
114
+ it "should return properties for attributes" do
115
+ @propset[:first_name].should be_instance_of(PollEverywhere::Serializable::Property)
116
+ end
117
+
118
+ it "should return a Value::Set" do
119
+ @propset.value_set.should be_instance_of(PollEverywhere::Serializable::Property::Value::Set)
120
+ end
121
+ end
122
+
123
+ describe PollEverywhere::Serializable::Property::Value::Set do
124
+ context "changes" do
125
+ before(:each) do
126
+ @prop = PollEverywhere::Serializable::Property.new(:email)
127
+ @value_set = PollEverywhere::Serializable::Property::Value::Set.new(@prop.value)
128
+ end
129
+
130
+ context "property value" do
131
+ before(:each) do
132
+ @value_set[:email] = 'brad@bradgessler.com'
133
+ end
134
+
135
+ it "should have the current value" do
136
+ @value_set[:email].should eql('brad@bradgessler.com')
137
+ end
138
+
139
+ it "should have the original value" do
140
+ @value_set.prop(:email).was.should be_nil
141
+ end
142
+
143
+ it "should have changes for property value" do
144
+ was, is = @value_set.prop(:email).changes
145
+ was.should be_nil
146
+ is.should eql('brad@bradgessler.com')
147
+ end
148
+
149
+ it "should have changed" do
150
+ @value_set.prop(:email).should be_changed
151
+ end
152
+ end
153
+
154
+ context "hash" do
155
+ it "should return hash of changed value" do
156
+ @value_set[:email] = 'brad@bradgessler.com'
157
+ @value_set.changes[:email].should eql([nil, 'brad@bradgessler.com'])
158
+ end
159
+ end
160
+ end
161
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polleverywhere
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-07-12 00:00:00.000000000 -07:00
12
+ date: 2011-07-15 00:00:00.000000000 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
17
- requirement: &2156511940 !ruby/object:Gem::Requirement
17
+ requirement: &2156434260 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: '0'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *2156511940
25
+ version_requirements: *2156434260
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rest-client
28
- requirement: &2156511340 !ruby/object:Gem::Requirement
28
+ requirement: &2156433200 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ~>
@@ -33,7 +33,7 @@ dependencies:
33
33
  version: 1.6.3
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *2156511340
36
+ version_requirements: *2156433200
37
37
  description: An easy way to integrate Poll Everywhere into your Ruby applications.
38
38
  email:
39
39
  - opensource@polleverywhere.com
@@ -68,7 +68,9 @@ files:
68
68
  - lib/polleverywhere/version.rb
69
69
  - polleverywhere.gemspec
70
70
  - spec/integration/api_spec.rb
71
+ - spec/lib/models_spec.rb
71
72
  - spec/lib/polleverywhere_spec.rb
73
+ - spec/lib/serializable_spec.rb
72
74
  - spec/spec_helper.rb
73
75
  has_rdoc: true
74
76
  homepage: http://www.github.com/polleverywhere/polleverywhere
@@ -97,5 +99,7 @@ specification_version: 3
97
99
  summary: Integrate Poll Everywhere into your Ruby applications
98
100
  test_files:
99
101
  - spec/integration/api_spec.rb
102
+ - spec/lib/models_spec.rb
100
103
  - spec/lib/polleverywhere_spec.rb
104
+ - spec/lib/serializable_spec.rb
101
105
  - spec/spec_helper.rb