page_record 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.rubocop.yml +13 -0
  2. data/CHANGES.md +5 -0
  3. data/Gemfile.lock +46 -14
  4. data/Guardfile +24 -0
  5. data/README.md +27 -1
  6. data/bin/autospec +16 -0
  7. data/bin/guard +16 -0
  8. data/bin/rake +16 -0
  9. data/bin/rspec +16 -0
  10. data/lib/page_record/attribute_accessors.rb +57 -54
  11. data/lib/page_record/base.rb +27 -22
  12. data/lib/page_record/class_actions.rb +47 -50
  13. data/lib/page_record/class_methods.rb +252 -261
  14. data/lib/page_record/errors.rb +81 -82
  15. data/lib/page_record/finders.rb +129 -131
  16. data/lib/page_record/form_builder.rb +4 -4
  17. data/lib/page_record/formtastic.rb +20 -9
  18. data/lib/page_record/helpers.rb +192 -131
  19. data/lib/page_record/inspector.rb +36 -0
  20. data/lib/page_record/instance_actions.rb +44 -46
  21. data/lib/page_record/rspec.rb +1 -1
  22. data/lib/page_record/validation.rb +46 -0
  23. data/lib/page_record/version.rb +1 -1
  24. data/lib/page_record.rb +13 -15
  25. data/page_record.gemspec +4 -1
  26. data/spec/.rubocop.yml +4 -0
  27. data/spec/helpers_spec.rb +109 -100
  28. data/spec/inspector_spec.rb +70 -0
  29. data/spec/page_record_spec.rb +357 -388
  30. data/spec/spec_helper.rb +1 -3
  31. data/spec/support/shared_contexts.rb +24 -2
  32. data/spec/support/shared_examples.rb +41 -45
  33. data/spec/support/team.rb +4 -4
  34. data/spec/support/test_app.rb +10 -13
  35. data/spec/support/views/page-with-1-error.erb +5 -0
  36. data/spec/support/views/page-with-2-errors-on-different-attributes.erb +9 -0
  37. data/spec/support/views/page-with-2-errors-on-same-attribute.erb +6 -0
  38. data/spec/support/views/page-without-errors.erb +4 -0
  39. data/spec/validation_spec.rb +142 -0
  40. data/tmp/rspec_guard_result +1 -0
  41. metadata +80 -5
@@ -1,262 +1,253 @@
1
1
  module PageRecord
2
- class Base
3
-
4
- # @private
5
- def self.inherited(base)
6
- base.class_eval do
7
- set_type_name(base)
8
- get_attribute_names
9
- end
10
- define_class_methods(base)
11
- define_instance_methods(base)
12
- end
13
-
14
-
15
- ##
16
- # Set's the page {PageRecord::Base} uses for all page operations.
17
- # when no parameter is given or the parameter is nil, just return the current value
18
- #
19
- # @param new_page [Cabybara::Session] The Capybara page
20
- #
21
- # @return [Capybara::Session]
22
- #
23
- def self.page (new_page = nil)
24
- new_page ? @@page = new_page : @@page
25
- end
26
-
27
- ##
28
- # Set's the page host class
29
- #
30
- # @param new_host_class an ActiveRecord like class
31
- #
32
- # @return [Class]
33
- #
34
- def self.host_class (new_host_class = nil)
35
- if new_host_class
36
- @host_class = new_host_class
37
- @host_name = new_host_class.to_s
38
- @type = @host_name.underscore
39
- get_attribute_names
40
- define_class_methods(self)
41
- define_instance_methods(self)
42
- end
43
- @host_class
44
- end
45
-
46
- ##
47
- # Set's the default selector for this class
48
- #
49
- # Example:
50
- #
51
- # ```ruby
52
- # class TeamPage < PageRecord::Base
53
- # selector "#first-table"
54
- # end
55
- #```
56
- # @param new_selector [String] The default selector to be used for all finders
57
- #
58
- def self.selector( new_selector)
59
- @selector = new_selector
60
- end
61
-
62
- ##
63
- # Set's the default filter for this class
64
- #
65
- #
66
- # Example:
67
- #
68
- # ```ruby
69
- # class TeamPage < PageRecord::Base
70
- # filter ".champions-league"
71
- # end
72
- #```
73
- #
74
- # @param new_filter [String] The default filter to be used for all finders
75
- #
76
- def self.filter( new_filter)
77
- @filter = new_filter
78
- end
79
-
80
- ##
81
- # Set's the default type for this class
82
- #
83
- # @param new_type [Symbol] The default type to be used for all finders. If type is nil just return the current type
84
- #
85
- # @return [Symbol] the type set.
86
- #
87
- # Example:
88
- #
89
- # ```ruby
90
- # class TopDivisonPage < PageRecord::Base
91
- # type :team
92
- # end
93
- #
94
- # TopDivisonPage.type # returns :team
95
- #```
96
- #
97
- #
98
- def self.type( new_type = nil)
99
- new_type ? @type = new_type : @type
100
- end
101
-
102
-
103
- ##
104
- # Set's the attributes this page recognises. This will override any types
105
- # inherited from the host class. When you don't specify a parameter, or a nil parameter
106
- # .attributes will return the current set of attributes
107
- #
108
- # @param new_attributes [Array] The attributes the page regognises
109
- #
110
- # @return [Array] returns the array of attributes the page recognises
111
- # Example:
112
- #
113
- # ```ruby
114
- # class TopDivisonPage < PageRecord::Base
115
- # attributes [:name, :position, :ranking]
116
- # end
117
- #```
118
- #
119
- #
120
- def self.attributes(new_attributes = nil)
121
- if new_attributes
122
- undefine_class_methods(self)
123
- undefine_instance_methods(self)
124
- @attributes = new_attributes
125
- define_class_methods(self)
126
- define_instance_methods(self)
127
- end
128
- @attributes
129
- end
130
-
131
-
132
- ##
133
- # Add some new attributes to the already availabe attributes
134
- #
135
- # @param extra_attributes [Array] The additional attributes the page regognises
136
- #
137
- # Example:
138
- #
139
- # ```ruby
140
- # class TopDivisonPage < PageRecord::Base
141
- # add_attributes [:full_name, :address_line]
142
- # end
143
- #```
144
- #
145
- #
146
- def self.add_attributes(extra_attributes)
147
- @attributes.concat(extra_attributes)
148
- # TODO check if we can optimise this to only add the new methods
149
- define_class_methods(self)
150
- define_instance_methods(self)
151
- @attributes
152
- end
153
-
154
-
155
- private
156
-
157
- # @private
158
- def self.set_type_name(base)
159
- begin
160
- @host_name = base.to_s.gsub('Page', '')
161
- @type = @host_name.underscore
162
- @host_class = @host_name.constantize
163
- rescue NameError
164
- @host_name = ''
165
- @host_class = ''
166
- end
167
- end
168
-
169
- # @private
170
- def self.get_attribute_names
171
- begin
172
- @attributes = @host_class.attribute_names.clone
173
- @attributes.delete('id') # id is a special case attribute
174
- rescue NameError
175
- @attributes = []
176
- end
177
- end
178
-
179
-
180
- # @private
181
- def self.define_accessor_methods(base)
182
- base.instance_eval do
183
- @attributes.each do | attribute |
184
- define_method("#{attribute}?") do
185
- read_attribute?(attribute)
186
- end
187
- define_method(attribute) do
188
- read_attribute(attribute)
189
- end
190
- define_method("#{attribute}=") do | value|
191
- write_attribute(attribute, value)
192
- end
193
- end
194
- end
195
- end
196
-
197
-
198
- # @private
199
- def self.undefine_accessor_methods(base)
200
- base.instance_eval do
201
- @attributes.each do | attribute |
202
- remove_method("#{attribute}?")
203
- remove_method(attribute)
204
- remove_method("#{attribute}=")
205
- end
206
- end
207
- end
208
-
209
-
210
- # @private
211
- def self.define_instance_methods(base)
212
- define_accessor_methods(base)
213
- end
214
-
215
- # @private
216
- def self.undefine_instance_methods(base)
217
- undefine_accessor_methods(base)
218
- end
219
-
220
-
221
- # @private
222
- def self.define_class_methods(base)
223
- eigenclass = class << base; self; end
224
- attributes = base.instance_variable_get('@attributes')
225
- eigenclass.instance_eval do
226
- attributes.each do | attribute|
227
- define_method "find_by_#{attribute}" do | value, selector = "", filter = ""|
228
- find_by_attribute( attribute, value, selector, filter)
229
- end
230
- end
231
- end
232
- end
233
-
234
- # @private
235
- def self.undefine_class_methods(base)
236
- eigenclass = class << base; self; end
237
- attributes = base.instance_variable_get('@attributes')
238
- eigenclass.instance_eval do
239
- attributes.each do | attribute|
240
- remove_method "find_by_#{attribute}"
241
- end
242
- end
243
- end
244
-
245
-
246
- # @private
247
- def self.context_for_selector(selector)
248
- if selector.blank?
249
- page
250
- else
251
- begin
252
- page.find(selector).find(:xpath, "..")
253
- rescue Capybara::Ambiguous
254
- raise MultipleRecords, "Found multiple HTML segments with selector #{selector} on page"
255
- rescue Capybara::ElementNotFound
256
- raise RecordNotFound, "#{selector} not found on page"
257
- end
258
- end
259
- end
260
-
261
- end
262
- end
2
+ class Base
3
+
4
+ ##
5
+ # Set's the default selector for this class
6
+ #
7
+ # Example:
8
+ #
9
+ # ```ruby
10
+ # class TeamPage < PageRecord::Base
11
+ # selector "#first-table"
12
+ # end
13
+ # ```
14
+ # @param new_selector [String] The default selector to be used for all finders
15
+ #
16
+ def self.selector( new_selector = nil)
17
+ @selector = new_selector if new_selector
18
+ @selector
19
+ end
20
+
21
+ ##
22
+ # Set's the default filter for this class
23
+ #
24
+ #
25
+ # Example:
26
+ #
27
+ # ```ruby
28
+ # class TeamPage < PageRecord::Base
29
+ # filter ".champions-league"
30
+ # end
31
+ # ```
32
+ #
33
+ # @param new_filter [String] The default filter to be used for all finders
34
+ #
35
+ def self.filter( new_filter = nil)
36
+ @filter = new_filter if new_filter
37
+ @filter
38
+ end
39
+
40
+ # @private
41
+ def self.inherited(base)
42
+ base.class_eval do
43
+ set_type_name(base)
44
+ get_attribute_names
45
+ end
46
+ define_class_methods(base)
47
+ define_instance_methods(base)
48
+ end
49
+
50
+ ##
51
+ # Set's the page {PageRecord::Base} uses for all page operations.
52
+ # when no parameter is given or the parameter is nil, just return the current value
53
+ #
54
+ # @param new_page [Cabybara::Session] The Capybara page
55
+ #
56
+ # @return [Capybara::Session]
57
+ #
58
+ # rubocop:disable AvoidClassVars:
59
+ def self.page (new_page = nil)
60
+ new_page ? @@page = new_page : @@page
61
+ end
62
+ # rubocop:enable AvoidClassVars:
63
+
64
+ ##
65
+ # Set's the page host class
66
+ #
67
+ # @param new_host_class an ActiveRecord like class
68
+ #
69
+ # @return [Class]
70
+ #
71
+ def self.host_class (new_host_class = nil)
72
+ if new_host_class
73
+ @host_class = new_host_class
74
+ @host_name = new_host_class.to_s
75
+ @type = @host_name.underscore
76
+ get_attribute_names
77
+ define_class_methods(self)
78
+ define_instance_methods(self)
79
+ end
80
+ @host_class
81
+ end
82
+
83
+ ##
84
+ # Set's the default type for this class
85
+ #
86
+ # @param new_type [Symbol] The default type to be used for all finders. If type is nil just return the current type
87
+ #
88
+ # @return [Symbol] the type set.
89
+ #
90
+ # Example:
91
+ #
92
+ # ```ruby
93
+ # class TopDivisonPage < PageRecord::Base
94
+ # type :team
95
+ # end
96
+ #
97
+ # TopDivisonPage.type # returns :team
98
+ # ```
99
+ #
100
+ #
101
+ def self.type(new_type = nil)
102
+ new_type ? @type = new_type : @type
103
+ end
104
+
105
+ ##
106
+ # Set's the attributes this page recognises. This will override any types
107
+ # inherited from the host class. When you don't specify a parameter, or a nil parameter
108
+ # .attributes will return the current set of attributes
109
+ #
110
+ # @param new_attributes [Array] The attributes the page regognises
111
+ #
112
+ # @return [Array] returns the array of attributes the page recognises
113
+ # Example:
114
+ #
115
+ # ```ruby
116
+ # class TopDivisonPage < PageRecord::Base
117
+ # attributes [:name, :position, :ranking]
118
+ # end
119
+ # ```
120
+ #
121
+ #
122
+ def self.attributes(new_attributes = nil)
123
+ if new_attributes
124
+ undefine_class_methods(self)
125
+ undefine_instance_methods(self)
126
+ @attributes = new_attributes
127
+ define_class_methods(self)
128
+ define_instance_methods(self)
129
+ end
130
+ @attributes
131
+ end
132
+
133
+ ##
134
+ # Add some new attributes to the already availabe attributes
135
+ #
136
+ # @param extra_attributes [Array] The additional attributes the page regognises
137
+ #
138
+ # Example:
139
+ #
140
+ # ```ruby
141
+ # class TopDivisonPage < PageRecord::Base
142
+ # add_attributes [:full_name, :address_line]
143
+ # end
144
+ # ```
145
+ #
146
+ #
147
+ def self.add_attributes(extra_attributes)
148
+ @attributes.concat(extra_attributes)
149
+ # TODO: check if we can optimise this to only add the new methods
150
+ define_class_methods(self)
151
+ define_instance_methods(self)
152
+ @attributes
153
+ end
154
+
155
+ private
156
+
157
+ # @private
158
+ def self.set_type_name(base)
159
+ @host_name = base.to_s.gsub('Page', '')
160
+ @type = @host_name.underscore
161
+ @host_class = @host_name.constantize
162
+ rescue NameError
163
+ @host_name = ''
164
+ @host_class = ''
165
+ end
166
+
167
+ # @private
168
+ def self.get_attribute_names
169
+ @attributes = @host_class.attribute_names.clone
170
+ @attributes.delete('id') # id is a special case attribute
171
+ rescue NameError
172
+ @attributes = []
173
+ end
174
+
175
+ # @private
176
+ def self.define_accessor_methods(base)
177
+ base.instance_eval do
178
+ @attributes.each do | attribute |
179
+ define_method("#{attribute}?") do
180
+ read_attribute?(attribute)
181
+ end
182
+ define_method(attribute) do
183
+ read_attribute(attribute)
184
+ end
185
+ define_method("#{attribute}=") do | value|
186
+ write_attribute(attribute, value)
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ # @private
193
+ def self.undefine_accessor_methods(base)
194
+ base.instance_eval do
195
+ @attributes.each do | attribute |
196
+ remove_method("#{attribute}?")
197
+ remove_method(attribute)
198
+ remove_method("#{attribute}=")
199
+ end
200
+ end
201
+ end
202
+
203
+ # @private
204
+ def self.define_instance_methods(base)
205
+ define_accessor_methods(base)
206
+ end
207
+
208
+ # @private
209
+ def self.undefine_instance_methods(base)
210
+ undefine_accessor_methods(base)
211
+ end
212
+
213
+ # @private
214
+ def self.define_class_methods(base)
215
+ eigenclass = class << base; self; end
216
+ attributes = base.instance_variable_get('@attributes')
217
+ eigenclass.instance_eval do
218
+ attributes.each do | attribute|
219
+ define_method "find_by_#{attribute}" do | value, selector = '', filter = ''|
220
+ find_by_attribute(attribute, value, selector, filter)
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ # @private
227
+ def self.undefine_class_methods(base)
228
+ eigenclass = class << base; self; end
229
+ attributes = base.instance_variable_get('@attributes')
230
+ eigenclass.instance_eval do
231
+ attributes.each do | attribute|
232
+ remove_method "find_by_#{attribute}"
233
+ end
234
+ end
235
+ end
236
+
237
+ # @private
238
+ def self.context_for_selector(selector)
239
+ if selector.blank?
240
+ page
241
+ else
242
+ begin
243
+ page.find(selector).find(:xpath, '..')
244
+ rescue Capybara::Ambiguous
245
+ raise MultipleRecords, "Found multiple HTML segments with selector #{selector} on page"
246
+ rescue Capybara::ElementNotFound
247
+ raise RecordNotFound, "#{selector} not found on page"
248
+ end
249
+ end
250
+ end
251
+
252
+ end
253
+ end