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 +1 -1
- data/.rspec +1 -1
- data/README.md +85 -2
- data/lib/formal_wear/class_methods.rb +5 -11
- data/lib/formal_wear/instance_methods.rb +61 -12
- data/lib/formal_wear/suit.rb +19 -0
- data/lib/formal_wear/version.rb +1 -1
- data/lib/formal_wear.rb +5 -2
- data/spec/bare_object_spec.rb +66 -0
- data/spec/completed_object_spec.rb +43 -0
- data/spec/new_object_spec.rb +248 -0
- data/spec/required_attr_spec.rb +15 -0
- data/spec/select_source_spec.rb +68 -0
- data/spec/support/bare_object.rb +2 -0
- data/spec/support/forbidden_option_tester.rb +10 -0
- data/spec/support/missing_option_tester.rb +7 -0
- data/spec/support/select_source.rb +13 -0
- data/spec/{test_formal_wear.rb → support/test_formal_wear.rb} +7 -1
- metadata +23 -6
- data/spec/test_formal_wear_spec.rb +0 -151
data/.coveralls.yml
CHANGED
@@ -1 +1 @@
|
|
1
|
-
service_name: travis-
|
1
|
+
service_name: travis-ci
|
data/.rspec
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
--color
|
2
|
-
--format
|
2
|
+
--format progress
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# FormalWear
|
2
2
|
|
3
|
-
[](https://travis-ci.org/wideopenspaces/formal_wear.png) [](https://codeclimate.com/github/wideopenspaces/formal_wear) [](https://coveralls.io/r/wideopenspaces/formal_wear)
|
3
|
+
[](https://travis-ci.org/wideopenspaces/formal_wear.png) [](https://codeclimate.com/github/wideopenspaces/formal_wear) [](https://coveralls.io/r/wideopenspaces/formal_wear) [](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
|
-
|
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(*
|
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(
|
7
|
-
|
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
|
13
|
-
|
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
|
-
|
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
|
data/lib/formal_wear/version.rb
CHANGED
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
|
@@ -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.
|
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.
|
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-
|
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/
|
117
|
-
- spec/
|
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/
|
145
|
-
- spec/
|
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
|