polleverywhere 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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