formal_wear 0.1.0 → 0.3.0

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/.coveralls.yml CHANGED
@@ -1 +1 @@
1
- service_name: travis-pro
1
+ service_name: travis-ci
data/.rspec CHANGED
@@ -1,2 +1,2 @@
1
1
  --color
2
- --format documentation
2
+ --format progress
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # FormalWear
2
2
 
3
- [![Build Status](https://travis-ci.org/wideopenspaces/formal_wear.png)](https://travis-ci.org/wideopenspaces/formal_wear.png) [![Code Climate](https://codeclimate.com/github/wideopenspaces/formal_wear.png)](https://codeclimate.com/github/wideopenspaces/formal_wear) [![Coverage Status](https://coveralls.io/repos/wideopenspaces/formal_wear/badge.png)](https://coveralls.io/r/wideopenspaces/formal_wear)
3
+ [![Build Status](https://travis-ci.org/wideopenspaces/formal_wear.png)](https://travis-ci.org/wideopenspaces/formal_wear.png) [![Code Climate](https://codeclimate.com/github/wideopenspaces/formal_wear.png)](https://codeclimate.com/github/wideopenspaces/formal_wear) [![Coverage Status](https://coveralls.io/repos/wideopenspaces/formal_wear/badge.png)](https://coveralls.io/r/wideopenspaces/formal_wear) [![Dependency Status](https://gemnasium.com/wideopenspaces/formal_wear.png)](https://gemnasium.com/wideopenspaces/formal_wear)
4
4
 
5
5
  "You're going to like the way you look. I guarantee it."
6
6
 
@@ -22,7 +22,90 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- TODO: Write usage instructions here
25
+ ```ruby
26
+ class TestFormalWear
27
+ include FormalWear
28
+
29
+ required_attr moms_id: {
30
+ name: "Your mom's id",
31
+ type: :text,
32
+ source: ->(s) { s.primary.thing_to_be_configured },
33
+ }
34
+
35
+ required_attr docs_id: {
36
+ name: "Your doc's id",
37
+ type: :text,
38
+ source: ->(s) { s.primary.dependent_object.another_thing_to_be_configured },
39
+ store: :set_my_docs_id
40
+ }
41
+
42
+ required_attr lambda_lambda_lambda: {
43
+ name: 'Revenge Of The Nerds!',
44
+ type: :text,
45
+ source: ->(s) { s.get_pledged },
46
+ store: ->(s) { s.got_lambda? }
47
+ }
48
+
49
+ protected
50
+
51
+ def after_save
52
+ # no-op
53
+ end
54
+
55
+ def set_moms_id
56
+ # no-op
57
+ end
58
+
59
+ def set_my_docs_id
60
+ # no-op
61
+ end
62
+
63
+ def got_lambda?
64
+ # no-op
65
+ end
66
+
67
+ def get_pledged
68
+ primary.dependent_object.another_thing_to_be_configured
69
+ end
70
+ end
71
+ ```
72
+
73
+ Including FormalWear into your class adds a `required_attrs` and `required_attr` methods.
74
+ Specifying a required attr, like so:
75
+
76
+ ```ruby
77
+ required_attr lambda_lambda_lambda: {
78
+ name: 'Revenge Of The Nerds!',
79
+ type: :text,
80
+ source: ->(s) { s.get_pledged },
81
+ store: ->(s) { s.got_lambda? }
82
+ }
83
+ ```
84
+
85
+ adds the attribute to the list of required_fields, and populates it from the source specified
86
+ in its hash when your class is initialized.
87
+
88
+ ### Instantiating
89
+
90
+ ```ruby
91
+ TestFormalWear.new(primary_object)
92
+ ```
93
+
94
+ ... where primary object you will use to fetch values from their source. (TODO: add support for multiple sources)
95
+
96
+ ### Specifying sources
97
+
98
+ The source option takes a lambda that accepts `self` as its argument. This allows you to use a
99
+ method within your class to fetch the information needed from its source, or to directly fetch it
100
+ using the `primary` object set when you instantiated the class.
101
+
102
+ Source is a required attribute, but it can return nil if you feel so inclined. It's a bit like going commando
103
+ under that tuxedo, though.
104
+
105
+ ## TODO
106
+
107
+ * Update this readme
108
+ * (Maybe?) Add helper methods for providing data for working with forms
26
109
 
27
110
  ## Contributing
28
111
 
@@ -1,15 +1,5 @@
1
1
  module FormalWear
2
2
  module ClassMethods
3
- def required_attrs(attrs)
4
- validate_attrs!(attrs)
5
-
6
- required = class_variable_get(:@@required_fields) || {}
7
- class_variable_set(:@@required_fields, required.merge(attrs))
8
-
9
- create_accessors(attrs.keys)
10
- end
11
- def required_attr(a); required_attrs(a); end
12
-
13
3
  def create_accessors(keys)
14
4
  keys.each { |k| self.send(:attr_accessor, k) unless method_defined?(k) }
15
5
  end
@@ -17,8 +7,12 @@ module FormalWear
17
7
  def validate_attrs!(attrs)
18
8
  attrs.each do |a, opts|
19
9
  opts.assert_valid_keys(*ALLOWED_KEYS)
20
- opts.assert_required_keys(*REQUIRED_KEYS)
10
+ opts.assert_required_keys(*required_keys(opts))
21
11
  end
22
12
  end
13
+
14
+ def required_keys(opts)
15
+ opts[:type] == :select ? REQUIRED_KEYS + [:select_options] : REQUIRED_KEYS
16
+ end
23
17
  end
24
18
  end
@@ -3,22 +3,36 @@ module FormalWear
3
3
  ### THE NITTY GRITTY STARTS HERE
4
4
 
5
5
  # Start the configuration process
6
- def initialize(primary)
7
- @primary = primary
6
+ def initialize(options = {})
7
+ set_sources(options)
8
8
  update_sources
9
9
  end
10
10
 
11
11
  def required_attributes
12
- required_fields.dup.each do |field, options|
13
- options.slice!(:name, :type)
12
+ sanitize_and_fill_attributes(required_fields)
13
+ end
14
+
15
+ def optional_attributes
16
+ sanitize_and_fill_attributes(optional_fields)
17
+ end
18
+
19
+ def attributes
20
+ {}.tap do |h|
21
+ h.merge!(required_attributes: required_attributes) if required_attributes
22
+ h.merge!(optional_attributes: optional_attributes) if optional_attributes
14
23
  end
15
24
  end
25
+ alias_method :to_h, :attributes
16
26
 
17
27
  # Return the list of required fields for this configurator
18
28
  def required_fields
19
29
  @@required_fields
20
30
  end
21
31
 
32
+ def optional_fields
33
+ @@optional_fields
34
+ end
35
+
22
36
  def required_fields_without_custom_stores
23
37
  required_fields.reject { |f,o| custom_store?(o) }
24
38
  end
@@ -27,16 +41,10 @@ module FormalWear
27
41
  required_fields.select { |f,o| custom_store?(o) }
28
42
  end
29
43
 
30
- def to_h
31
- {}.merge(required_attributes: required_attributes).tap do |h|
32
- required_fields.keys.each { |k| h[k] = self.send(k) }
33
- end
34
- end
35
-
36
44
  def update(attrs)
37
- raise ArgumentError, "update requires a Hash"
45
+ raise ArgumentError, "update requires a Hash" unless attrs.is_a?(Hash)
38
46
  attrs.each do |field, value|
39
- self.send(:"#{field}=", value) if instance_method_defined?(field)
47
+ send(:"#{field}=", value) if respond_to?(field)
40
48
  end
41
49
  end
42
50
 
@@ -67,6 +75,26 @@ module FormalWear
67
75
  after_save
68
76
  end
69
77
 
78
+ def sanitize_and_fill_attributes(fields)
79
+ fields.deep_dup.each do |field, options|
80
+ fill_attributes!(field, options)
81
+ sanitize_attributes!(options)
82
+ end
83
+ end
84
+
85
+ def sanitize_attributes!(options)
86
+ options.slice!(:name, :type, :value, :values)
87
+ end
88
+
89
+ def fill_attributes!(field, options)
90
+ options[:value] = self.send(field)
91
+ populate_select_values(options) if options[:type] == :select
92
+ end
93
+
94
+ def populate_select_values(options)
95
+ options[:values] = options[:select_options].call(self)
96
+ end
97
+
70
98
  def set_field(field, options)
71
99
  custom_store?(options) ? set_via_custom_store(field, options) : self.send(:"set_#{field}")
72
100
  end
@@ -97,6 +125,27 @@ module FormalWear
97
125
  custom_store?(options) && options[:store].try(:lambda?)
98
126
  end
99
127
 
128
+ def set_sources(options)
129
+ set_primary(options) and return unless options.is_a?(Hash)
130
+ if options[:sources].present?
131
+ @sources = options[:sources]
132
+ set_source_readers
133
+ end
134
+ end
135
+
136
+ def set_primary(source)
137
+ @primary = source
138
+ end
139
+
140
+ def set_source_readers
141
+ @sources.each { |alt, obj| set_source_reader(alt, obj) }
142
+ end
143
+
144
+ def set_source_reader(alt, obj)
145
+ class_eval { attr_reader alt }
146
+ instance_variable_set(:"@#{alt}", obj)
147
+ end
148
+
100
149
  # When donning formal_wear, pre-populate from source if available
101
150
  def update_sources
102
151
  required_fields.each do |field, options|
@@ -0,0 +1,19 @@
1
+ module FormalWear
2
+ module Suit
3
+ def self.up(klass)
4
+ class << klass
5
+ [:optional, :required].each do |kind|
6
+ define_method :"#{kind}_attrs" do |attrs|
7
+ validate_attrs!(attrs)
8
+
9
+ var = class_variable_get(:"@@#{kind}_fields") || {}
10
+ class_variable_set(:"@@#{kind}_fields", var.merge(attrs))
11
+
12
+ create_accessors(attrs.keys)
13
+ end
14
+ alias :"#{kind}_attr" :"#{kind}_attrs"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module FormalWear
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/formal_wear.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'formal_wear/version'
2
2
  require 'formal_wear/class_methods'
3
3
  require 'formal_wear/instance_methods'
4
+ require 'formal_wear/suit'
4
5
  require 'hash_assertions'
5
6
  require 'active_support/core_ext'
6
7
  require 'active_support/concern'
@@ -17,6 +18,8 @@ module FormalWear
17
18
  include FormalWear::InstanceMethods
18
19
 
19
20
  attr_reader :primary
20
- cattr_reader :required_fields
21
+ cattr_reader :required_fields, :optional_fields
22
+
23
+ FormalWear::Suit.up(self)
21
24
  end
22
- end
25
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+ require_relative './support/bare_object'
3
+
4
+ describe BareObject do
5
+ let(:obj) { BareObject }
6
+
7
+ context 'including FormalWear' do
8
+ before { obj.send(:include, FormalWear) }
9
+
10
+ context 'when required_attr is invoked' do
11
+ before do
12
+ obj.class_eval do
13
+ required_attr dynamic_id: {
14
+ name: "Test",
15
+ type: :text,
16
+ source: ->(s) { nil },
17
+ }
18
+ end
19
+ end
20
+
21
+ it 'sets a class variable called @@required_fields' do
22
+ obj.class_variable_defined?(:@@required_fields).should be_true
23
+ end
24
+
25
+ it 'adds the given field to @@required_fields' do
26
+ obj.class_variable_get(:@@required_fields).keys.should include(:dynamic_id)
27
+ end
28
+
29
+ it 'adds a reader for the field' do
30
+ obj.method_defined?(:dynamic_id).should be_true
31
+ end
32
+
33
+ it 'adds a writer for the field' do
34
+ obj.method_defined?(:dynamic_id=).should be_true
35
+ end
36
+ end
37
+
38
+ context 'when optional_attr is invoked' do
39
+ before do
40
+ obj.class_eval do
41
+ optional_attr optional_id: {
42
+ name: "Test",
43
+ type: :text,
44
+ source: ->(s) { nil },
45
+ }
46
+ end
47
+ end
48
+
49
+ it 'sets a class variable called @@optional_attr' do
50
+ obj.class_variable_defined?(:@@optional_fields).should be_true
51
+ end
52
+
53
+ it 'adds the given field to @@optional_attr' do
54
+ obj.class_variable_get(:@@optional_fields).keys.should include(:optional_id)
55
+ end
56
+
57
+ it 'adds a reader for the field' do
58
+ obj.method_defined?(:optional_id).should be_true
59
+ end
60
+
61
+ it 'adds a writer for the field' do
62
+ obj.method_defined?(:optional_id=).should be_true
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require_relative './support/test_formal_wear'
3
+
4
+ describe 'a configurator with completed setup' do
5
+ let(:ready_primary) { ExternalObjectOne.new('Formal', 'Wear') }
6
+ let(:ready_config) { TestFormalWear.new(ready_primary) }
7
+
8
+ subject { ready_config }
9
+
10
+ context 'when initialized' do
11
+ describe '#moms_id' do
12
+ it 'is equal to ExternalObjectOne.thing_to_be_configured' do
13
+ subject.moms_id.should == subject.primary.thing_to_be_configured
14
+ end
15
+ end
16
+
17
+ describe '#docs_id' do
18
+ it 'is equal to ExternalObjectTwo.another_thing_to_be_configured' do
19
+ subject.docs_id.should == subject.primary.dependent_object.another_thing_to_be_configured
20
+ end
21
+ end
22
+
23
+ describe '#lambda_lambda_lambda' do
24
+ it 'equals ExternalObjectTwo.yet_another_thing_to_be_configured' do
25
+ subject.lambda_lambda_lambda = subject.primary.dependent_object.yet_another_thing_to_be_configured
26
+ end
27
+
28
+ it 'equals the method cited in its source attr' do
29
+ subject.lambda_lambda_lambda = subject.send(:get_pledged)
30
+ end
31
+ end
32
+
33
+ describe '#i_am_optional' do
34
+ it 'is defined' do
35
+ subject.respond_to?(:i_am_optional).should be_true
36
+ end
37
+
38
+ it 'is nil' do
39
+ subject.i_am_optional.should be_nil
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,248 @@
1
+ require 'spec_helper'
2
+ require_relative './support/test_formal_wear'
3
+
4
+ describe 'a new FormalWear-ing object' do
5
+ let(:primary) { ExternalObjectOne.new(nil, nil)}
6
+ let(:config) { TestFormalWear.new(primary) }
7
+
8
+ subject { config }
9
+
10
+ it 'has a required_fields method' do
11
+ subject.should respond_to(:required_fields)
12
+ end
13
+
14
+ describe '#required_fields' do
15
+ subject { config.required_fields }
16
+ it { should be_a(Hash) }
17
+
18
+ it "should mirror class's @@required_fields" do
19
+ subject.should == config.class.class_variable_get(:@@required_fields)
20
+ end
21
+
22
+ it 'contains the appropriate required fields' do
23
+ subject.keys.should =~ [:moms_id, :docs_id, :lambda_lambda_lambda]
24
+ end
25
+ end
26
+
27
+ describe '#optional_fields' do
28
+ subject { config.optional_fields }
29
+ it { should be_a(Hash) }
30
+
31
+ it "should mirror class's @@optional_fields" do
32
+ subject.should == config.class.class_variable_get(:@@optional_fields)
33
+ end
34
+
35
+ it 'contains the appropriate optional fields' do
36
+ subject.keys.should =~ [:i_am_optional]
37
+ end
38
+ end
39
+
40
+ it 'has a valid? method' do
41
+ subject.should respond_to(:valid?)
42
+ end
43
+
44
+ it 'has a getter method for each of the required fields' do
45
+ subject.required_fields.keys.each do |k|
46
+ subject.should respond_to(k)
47
+ end
48
+ end
49
+
50
+ it 'has a setter method for each of the required fields' do
51
+ subject.required_fields.keys.each do |k|
52
+ subject.should respond_to(:"#{k}=")
53
+ end
54
+ end
55
+
56
+ context 'when a required field has a "store" key' do
57
+ context 'that is a symbol' do
58
+ it 'responds to a method corresponding to that symbol' do
59
+ subject.should respond_to(subject.required_fields[:docs_id][:store])
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'when a required field does not have a "store" key' do
65
+ it 'responds to a set_xxx method for each required field' do
66
+ config.required_fields_without_custom_stores.each do |field, options|
67
+ subject.should respond_to(:"set_#{field}")
68
+ end
69
+ end
70
+ end
71
+
72
+ context 'when supplied with only one source object' do
73
+ let(:config) { TestFormalWear.new(ExternalObjectTwo.new(nil)) }
74
+
75
+ it 'sets an instance variable called primary containing the source' do
76
+ config.instance_variable_defined?(:@primary).should be_true
77
+ end
78
+
79
+ it 'allows access to the primary object correctly' do
80
+ config.primary.yet_another_thing_to_be_configured.should == 'Stuff'
81
+ end
82
+ end
83
+
84
+ context 'when supplied with a Hash of source objects' do
85
+ let(:config) { TestFormalWear.new(sources: {
86
+ object_two: ExternalObjectTwo.new("I am a secondary") } ) }
87
+
88
+ it 'sets an instance variable called sources containing the sources' do
89
+ config.instance_variable_defined?(:@sources).should be_true
90
+ end
91
+
92
+ it 'sets instance variables for each source object by name' do
93
+ config.instance_variable_defined?(:@object_two).should be_true
94
+ end
95
+
96
+ it 'sets a reader for each source object' do
97
+ config.respond_to?(:object_two).should be_true
98
+ end
99
+
100
+ it 'allows access to the source object correctly' do
101
+ config.object_two.another_thing_to_be_configured.should == "I am a secondary"
102
+ end
103
+ end
104
+
105
+ describe '#attributes' do
106
+ context 'given an object that contains required and optional attrs' do
107
+ it 'returns a hash containing appropriate keys' do
108
+ config.attributes.keys.should include(:required_attributes, :optional_attributes)
109
+ end
110
+ end
111
+ end
112
+
113
+ describe '#required_attributes' do
114
+ it 'removes all options except name, type and value' do
115
+ config.required_attributes.each do |r,o|
116
+ o.should_not include([:source, :store, :select_options])
117
+ end
118
+ end
119
+
120
+ it 'adds value to the options' do
121
+ config.required_attributes.each do |r,o|
122
+ o.should include(:value)
123
+ end
124
+ end
125
+
126
+ context 'given type :text' do
127
+ it 'does not add values to the options' do
128
+ config.required_attributes.each do |r,o|
129
+ o.should_not include(:values)
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ describe '#optional_attributes' do
136
+ it 'removes all options except name, type and value' do
137
+ config.optional_attributes.each do |r,o|
138
+ o.should_not include([:source, :store, :select_options])
139
+ end
140
+ end
141
+
142
+ it 'adds value to the options' do
143
+ config.optional_attributes.each do |r,o|
144
+ o.should include(:value)
145
+ end
146
+ end
147
+ end
148
+
149
+ describe '#valid?' do
150
+ subject { config }
151
+
152
+ context 'when none of the required fields are set' do
153
+ before do
154
+ config.moms_id = nil
155
+ end
156
+
157
+ it 'returns false' do
158
+ config.valid?.should be_false
159
+ end
160
+ end
161
+
162
+ context 'when not all of the required fields are set' do
163
+ before do
164
+ config.moms_id = 'zztop'
165
+ end
166
+
167
+ it 'returns false' do
168
+ config.valid?.should be_false
169
+ end
170
+ end
171
+
172
+ context 'when all of the required fields are set' do
173
+ before do
174
+ config.moms_id = config.docs_id = config.lambda_lambda_lambda = '1'
175
+ end
176
+
177
+ it 'returns true' do
178
+ config.valid?.should be_true
179
+ end
180
+ end
181
+ end
182
+
183
+ describe '#update' do
184
+ context 'given anything other than a hash' do
185
+ subject { config.update("Bob") }
186
+
187
+ it 'raises an ArgumentError' do
188
+ expect { subject }.to raise_error(ArgumentError, 'update requires a Hash' )
189
+ end
190
+ end
191
+
192
+ context 'given a hash of params' do
193
+ subject { config.update(docs_id: 1, nonexistent_id: 4) }
194
+ it 'does not raise an error' do
195
+ expect { subject }.to_not raise_error(ArgumentError)
196
+ end
197
+
198
+ it 'updates local attributes based on input' do
199
+ subject
200
+ config.docs_id.should == 1
201
+ end
202
+
203
+ it 'should ignore attributes that do not exist' do
204
+ subject
205
+ config.try(:nonexistent_id).should be_nil
206
+ end
207
+ end
208
+ end
209
+
210
+ describe '#save' do
211
+ subject { config }
212
+
213
+ context 'when not valid?' do
214
+ it 'returns false' do
215
+ subject.save.should be_false
216
+ end
217
+ end
218
+
219
+ context 'when valid?' do
220
+ before do
221
+ subject.moms_id = subject.docs_id = subject.lambda_lambda_lambda = '1'
222
+ subject.required_fields_without_custom_stores.keys.each do |k|
223
+ subject.expects("set_#{k}").returns(true)
224
+ end
225
+
226
+ subject.required_fields_with_custom_stores.each do |field, opt|
227
+ if opt[:store].is_a?(Symbol)
228
+ subject.expects(opt[:store]).returns(true)
229
+ subject.expects(:"set_#{field}").never
230
+ elsif opt[:store].try(:lambda?)
231
+ subject.expects(:got_lambda?).returns(true)
232
+ end
233
+ end
234
+
235
+ subject.expects(:after_save).returns(true)
236
+ end
237
+
238
+ it 'calls the set_xxx methods for each required field' do
239
+ subject.save
240
+ end
241
+
242
+ it 'calls #after_save' do
243
+ # condition met by expectation above
244
+ subject.save
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Class#required_attr method' do
4
+ context 'when given unallowed field options' do
5
+ it 'raises an ArgumentError' do
6
+ expect { require_relative('./support/forbidden_option_tester') }.to raise_error(ArgumentError, "Unknown key: extraneous")
7
+ end
8
+ end
9
+
10
+ context 'when missing required field options' do
11
+ it 'raises an ArgumentError' do
12
+ expect { require_relative('./support/missing_option_tester') }.to raise_error(ArgumentError, /Missing required key:/)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+ require_relative './support/bare_object'
3
+ require_relative './support/select_source'
4
+
5
+ describe BareObject do
6
+ let(:obj) { BareObject }
7
+
8
+ context 'including FormalWear' do
9
+ before { obj.send(:include, FormalWear) }
10
+
11
+ context 'when required_attr is invoked' do
12
+ context 'with a field of type :select' do
13
+ context 'without select_options specified' do
14
+ it 'should raise an error' do
15
+ expect {
16
+ obj.class_eval do
17
+ required_attr unselect_me: {
18
+ name: 'Broken select',
19
+ type: :select,
20
+ source: ->(s) { SelectSource.first },
21
+ }
22
+ end
23
+ }.to raise_error
24
+ end
25
+ end
26
+
27
+ context 'with select_options specified' do
28
+ before do
29
+ obj.class_eval do
30
+ required_attr select_me: {
31
+ name: "Selected",
32
+ type: :select,
33
+ source: ->(s) { SelectSource.first },
34
+ select_options: ->(s) { SelectSource.all }
35
+ }
36
+ end
37
+ end
38
+
39
+ context 'a new BareObject' do
40
+ subject { BareObject.new }
41
+ context '#required_attributes' do
42
+ it 'includes select_me' do
43
+ subject.required_attributes.keys.should include(:select_me)
44
+ end
45
+
46
+ context '[:select_me]' do
47
+ let(:new_obj) { BareObject.new }
48
+ let(:select_me) { new_obj.required_attributes[:select_me] }
49
+ subject { select_me }
50
+
51
+ it 'includes a values key' do
52
+ subject.keys.should include(:values)
53
+ end
54
+
55
+ context '[:values]' do
56
+ subject { select_me[:values] }
57
+ it 'contains the data specified in the select_options key' do
58
+ subject.should == SelectSource.all
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,2 @@
1
+ class BareObject
2
+ end
@@ -0,0 +1,10 @@
1
+ class ForbiddenOptionTester
2
+ include FormalWear
3
+
4
+ required_attr moms_id: {
5
+ name: "Your mom's id",
6
+ type: :text,
7
+ source: ->(s) { s.primary.thing_to_be_configured },
8
+ extraneous: :thing
9
+ }
10
+ end
@@ -0,0 +1,7 @@
1
+ class MissingOptionTester
2
+ include FormalWear
3
+
4
+ required_attr moms_id: {
5
+ name: "Your mom's id"
6
+ }
7
+ end
@@ -0,0 +1,13 @@
1
+ class SelectSource
2
+ def self.all
3
+ [
4
+ { id: 1, name: 'Option 1' },
5
+ { id: 2, name: 'Option 2' },
6
+ { id: 3, name: 'Option 3' }
7
+ ]
8
+ end
9
+
10
+ def self.first
11
+ all.first
12
+ end
13
+ end
@@ -23,6 +23,12 @@ class TestFormalWear
23
23
  store: ->(s) { s.got_lambda? }
24
24
  }
25
25
 
26
+ optional_attr i_am_optional: {
27
+ name: 'Test optional',
28
+ type: 'text',
29
+ source: ->(s) { nil }
30
+ }
31
+
26
32
  protected
27
33
 
28
34
  def after_save
@@ -42,7 +48,7 @@ class TestFormalWear
42
48
  end
43
49
 
44
50
  def get_pledged
45
- primary.dependent_object.another_thing_to_be_configured
51
+ primary.dependent_object.yet_another_thing_to_be_configured
46
52
  end
47
53
  end
48
54
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: formal_wear
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-03 00:00:00.000000000 Z
12
+ date: 2013-07-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -111,10 +111,19 @@ files:
111
111
  - lib/formal_wear.rb
112
112
  - lib/formal_wear/class_methods.rb
113
113
  - lib/formal_wear/instance_methods.rb
114
+ - lib/formal_wear/suit.rb
114
115
  - lib/formal_wear/version.rb
116
+ - spec/bare_object_spec.rb
117
+ - spec/completed_object_spec.rb
118
+ - spec/new_object_spec.rb
119
+ - spec/required_attr_spec.rb
120
+ - spec/select_source_spec.rb
115
121
  - spec/spec_helper.rb
116
- - spec/test_formal_wear.rb
117
- - spec/test_formal_wear_spec.rb
122
+ - spec/support/bare_object.rb
123
+ - spec/support/forbidden_option_tester.rb
124
+ - spec/support/missing_option_tester.rb
125
+ - spec/support/select_source.rb
126
+ - spec/support/test_formal_wear.rb
118
127
  homepage: https://github.com/wideopenspaces/formal_wear
119
128
  licenses: []
120
129
  post_install_message:
@@ -140,6 +149,14 @@ signing_key:
140
149
  specification_version: 3
141
150
  summary: You're going to like the way you look. I guarantee it.
142
151
  test_files:
152
+ - spec/bare_object_spec.rb
153
+ - spec/completed_object_spec.rb
154
+ - spec/new_object_spec.rb
155
+ - spec/required_attr_spec.rb
156
+ - spec/select_source_spec.rb
143
157
  - spec/spec_helper.rb
144
- - spec/test_formal_wear.rb
145
- - spec/test_formal_wear_spec.rb
158
+ - spec/support/bare_object.rb
159
+ - spec/support/forbidden_option_tester.rb
160
+ - spec/support/missing_option_tester.rb
161
+ - spec/support/select_source.rb
162
+ - spec/support/test_formal_wear.rb
@@ -1,151 +0,0 @@
1
- require 'spec_helper'
2
- require 'test_formal_wear'
3
-
4
- describe TestFormalWear do
5
- let(:primary) { ExternalObjectOne.new(nil, nil)}
6
- let(:config) { TestFormalWear.new(primary) }
7
-
8
- describe 'a configurator with completed setup' do
9
- let(:ready_primary) { ExternalObjectOne.new('Formal', 'Wear') }
10
- let(:ready_config) { TestFormalWear.new(ready_primary) }
11
-
12
- subject { ready_config }
13
-
14
- context 'when initialized' do
15
- describe '#moms_id' do
16
- it 'is equal to ExternalObjectOne.thing_to_be_configured' do
17
- subject.moms_id.should == subject.primary.thing_to_be_configured
18
- end
19
- end
20
-
21
- describe '#docs_id' do
22
- it 'is equal to ExternalObjectTwo.another_thing_to_be_configured' do
23
- subject.docs_id.should == subject.primary.dependent_object.another_thing_to_be_configured
24
- end
25
- end
26
- end
27
- end
28
-
29
- describe 'a new configurator object' do
30
- subject { config }
31
-
32
- it 'has a required_fields method' do
33
- subject.should respond_to(:required_fields)
34
- end
35
-
36
- describe '#required_fields' do
37
- subject { config.required_fields }
38
- it { should be_a(Hash) }
39
-
40
- it 'contains the appropriate required fields' do
41
- subject.keys.should =~ [:moms_id, :docs_id, :lambda_lambda_lambda]
42
- end
43
- end
44
-
45
- it 'has a valid? method' do
46
- subject.should respond_to(:valid?)
47
- end
48
-
49
- it 'has a getter method for each of the required fields' do
50
- subject.required_fields.keys.each do |k|
51
- subject.should respond_to(k)
52
- end
53
- end
54
-
55
- it 'has a setter method for each of the required fields' do
56
- subject.required_fields.keys.each do |k|
57
- subject.should respond_to(:"#{k}=")
58
- end
59
- end
60
-
61
- context 'when a required field has a "store" key' do
62
- context 'that is a symbol' do
63
- it 'responds to a method corresponding to that symbol' do
64
- subject.should respond_to(subject.required_fields[:docs_id][:store])
65
- end
66
- end
67
- end
68
-
69
- context 'when a required field does not have a "store" key' do
70
- it 'responds to a set_xxx method for each required field' do
71
- config.required_fields_without_custom_stores.each do |field, options|
72
- subject.should respond_to(:"set_#{field}")
73
- end
74
- end
75
- end
76
- end
77
-
78
- describe '#valid?' do
79
- subject { config }
80
-
81
- context 'when none of the required fields are set' do
82
- before do
83
- config.moms_id = nil
84
- end
85
-
86
- it 'returns false' do
87
- config.valid?.should be_false
88
- end
89
- end
90
-
91
- context 'when not all of the required fields are set' do
92
- before do
93
- config.moms_id = 'zztop'
94
- end
95
-
96
- it 'returns false' do
97
- config.valid?.should be_false
98
- end
99
- end
100
-
101
- context 'when all of the required fields are set' do
102
- before do
103
- config.moms_id = config.docs_id = config.lambda_lambda_lambda = '1'
104
- end
105
-
106
- it 'returns true' do
107
- config.valid?.should be_true
108
- end
109
- end
110
- end
111
-
112
- describe '#save' do
113
- subject { config }
114
-
115
- context 'when not valid?' do
116
- it 'returns false' do
117
- subject.save.should be_false
118
- end
119
- end
120
-
121
- context 'when valid?' do
122
- before do
123
- subject.moms_id = subject.docs_id = subject.lambda_lambda_lambda = '1'
124
- subject.required_fields_without_custom_stores.keys.each do |k|
125
- subject.expects("set_#{k}").returns(true)
126
- end
127
-
128
- subject.required_fields_with_custom_stores.each do |field, opt|
129
- if opt[:store].is_a?(Symbol)
130
- subject.expects(opt[:store]).returns(true)
131
- subject.expects(:"set_#{field}").never
132
- elsif opt[:store].try(:lambda?)
133
- subject.expects(:got_lambda?).returns(true)
134
- end
135
- end
136
-
137
- subject.expects(:after_save).returns(true)
138
- end
139
-
140
- it 'calls the set_xxx methods for each required field' do
141
- subject.save
142
- end
143
-
144
- it 'calls #after_save' do
145
- # condition met by expectation above
146
- subject.save
147
- end
148
- end
149
- end
150
-
151
- end