cave 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'pry'
7
+ gem 'rspec'
8
+ end
@@ -0,0 +1,66 @@
1
+ Cave
2
+ =====
3
+
4
+ A simple Ruby form library, intended for use with Rails
5
+
6
+ Largely inspired by Code Climate's [7 Patterns to Refactor Fat ActiveRecord Models](http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/) as a method for encapsulating validation into an object with a single responsibility.
7
+
8
+ ### Forms
9
+
10
+ [Cave::Form](https://github.com/jamesdabbs/cave/blob/master/lib/cave/form.rb) is a light glue layer around
11
+ [Rails' ActiveModel::Validations](http://guides.rubyonrails.org/active_record_validations_callbacks.html)
12
+ and [Virtus](https://github.com/solnic/virtus)' type coercion. You may want to consult their documentation for
13
+ more options.
14
+
15
+ class FormClass < Cave::Form
16
+ field :name, String,
17
+ presence: true,
18
+ format: { :with => /\A[a-zA-Z]+\z/, :message => "Only letters allowed" }
19
+ # Takes any number of standard Rails validation helper options
20
+ field :favorite_number, Integer,
21
+ inclusion: { :in => 1..10 }
22
+
23
+ def persist!
24
+ # Define your persistence logic here.
25
+ # This method will be called whenever a valid form is saved.
26
+ "Form saved!"
27
+ end
28
+ end
29
+
30
+ form = FormClass.bind name: 'James Dabbs', favorite_number: 11
31
+ form.valid?
32
+ => false
33
+ form.errors.full_messages
34
+ => ["Name Only letters allowed", "Favorite number is not included in the list"]
35
+
36
+ form = FormClass.bind name: 'jamesdabbs', favorite_number: '7'
37
+ form.valid?
38
+ => true
39
+ form.favorite_number
40
+ => 7 // Note the type coercion
41
+ form.save!
42
+ => "Form saved!"
43
+
44
+ ###Forms for Models
45
+
46
+ For the common use case of creating or updating a model with a form, Cave provides
47
+ the [Cave::ModelForm](https://github.com/jamesdabbs/cave/blob/master/lib/cave/model_form.rb) class.
48
+
49
+ class ProfileForm < Cave::ModelForm
50
+ model Profile
51
+
52
+ field :name, String, presence: true
53
+ field :age, Integer
54
+ end
55
+
56
+ ProfileForm.bind(name: 'James').save! # Creates a new Profile named 'James'
57
+
58
+ instance = Profile.first
59
+ ProfileForm.bind(instance, name: 'Jim').save! # Updates the Profile's name
60
+
61
+ ###Planned features
62
+
63
+ - Add presenters for rendering forms into html (inc. a bootstrap template)
64
+ - Improve handling of intial values (as for unbound ModelForms)
65
+ - Write docs
66
+ - Requests?
@@ -0,0 +1,8 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :test => :spec
8
+ task :default => :spec
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'cave'
3
+ s.version = '0.0.1'
4
+ s.date = '2013-01-31'
5
+ s.summary = 'A simple Ruby form library, intended for use with Rails'
6
+ s.description = 'Cave Forms encapsulate validation logic into an object with that single responsibility'
7
+ s.authors = ['James Dabbs']
8
+ s.email = 'jamesdabbs@gmail.com'
9
+ s.files = %w{ Gemfile Rakefile README.md cave.gemspec }
10
+ s.files += Dir['lib/**/*.rb']
11
+ s.test_files = Dir['spec/**/*.rb']
12
+ s.homepage = 'http://github.com/jamesdabbs/cave'
13
+
14
+ s.add_dependency 'activemodel'
15
+ s.add_dependency 'virtus'
16
+ end
@@ -0,0 +1,6 @@
1
+ require 'cave/errors'
2
+ require 'cave/form'
3
+ require 'cave/model_form'
4
+
5
+ module Cave
6
+ end
@@ -0,0 +1,4 @@
1
+ module Cave
2
+ class ValidationError < Exception
3
+ end
4
+ end
@@ -0,0 +1,75 @@
1
+ require 'active_model'
2
+ require 'virtus'
3
+
4
+ module Cave
5
+ class Form
6
+ include Virtus
7
+ include ActiveModel::Validations
8
+
9
+ class << self
10
+
11
+ def fields
12
+ @_fields ||= {}
13
+ end
14
+
15
+ def field name, type, opts={}
16
+ @_fields ||= {}
17
+ @_fields[name] = type
18
+
19
+ attribute name, type
20
+
21
+ opts.each do |k,v|
22
+ validates name, {k => v}
23
+ end
24
+ end
25
+
26
+ def bind *args
27
+ f = new *args
28
+ f.instance_eval { @bound = true }
29
+ f
30
+ end
31
+
32
+ end
33
+
34
+ validate :field_coercion
35
+
36
+ def initialize attrs={}
37
+ super
38
+ bind attrs unless attrs.empty?
39
+ end
40
+
41
+ def bind attrs={}
42
+ self.attributes = (attributes || {}).merge attrs
43
+ @bound = true
44
+ end
45
+
46
+ def bound?
47
+ @bound
48
+ end
49
+
50
+ def unbound?
51
+ !@bound
52
+ end
53
+
54
+ def save!
55
+ raise Cave::ValidationError.new errors.full_messages.join(',') unless valid?
56
+ persist!
57
+ end
58
+
59
+ def persist!
60
+ raise "#{self.class} does not define a persist method"
61
+ end
62
+
63
+ private #----------
64
+
65
+ def field_coercion
66
+ self.class.fields.each do |name, type|
67
+ value = attributes[name]
68
+ unless value.nil? || value.is_a?(type)
69
+ errors.add name, "should be a(n) #{type}"
70
+ end
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,37 @@
1
+ module Cave
2
+ class ModelForm < Form
3
+ def self.model klass=nil
4
+ @@model ||= klass
5
+ end
6
+
7
+ def initialize instance=nil, attrs={}
8
+ (instance, attrs = nil, instance) if instance.is_a? Hash
9
+ super attrs
10
+ self.for instance
11
+ end
12
+
13
+ def for instance
14
+ @instance = instance
15
+ check_instance_model
16
+ end
17
+
18
+ def persist!
19
+ if @instance
20
+ @instance.update_attributes attributes
21
+ else
22
+ @instance = self.class.model.create! attributes
23
+ end
24
+ end
25
+
26
+ private #-----------
27
+
28
+ def check_instance_model
29
+ model = self.class.model
30
+ if @instance
31
+ raise TypeError.new("Instance #{@instance} is not a #{model}") unless @instance.is_a? model
32
+ else
33
+ raise TypeError.new("Please specify a model class to create") unless model.is_a? Class
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,77 @@
1
+ require 'helper'
2
+
3
+ class ExampleForm < Cave::Form
4
+ field :name, String, presence: true
5
+ field :age, Integer
6
+ end
7
+
8
+ describe ExampleForm do
9
+ context 'when unbound' do
10
+ subject { ExampleForm.new }
11
+
12
+ it { should_not be_bound }
13
+ it { should_not be_valid }
14
+ end
15
+
16
+ context 'when bound' do
17
+ subject { ExampleForm.bind name: 'James', age: 26 }
18
+
19
+ it { should be_bound }
20
+ its(:name) { should == 'James' }
21
+
22
+ it 'validates name' do
23
+ subject.name = ''
24
+ subject.should_not be_valid
25
+ end
26
+
27
+ it 'coerces age' do
28
+ subject.age = '27'
29
+ subject.age.should be 27
30
+ end
31
+
32
+ it 'fails on uncoercible ages' do
33
+ subject.age = 'invalid'
34
+ subject.should_not be_valid
35
+ end
36
+
37
+ it 'allows uncoerced nils' do
38
+ subject.age = nil
39
+ subject.should be_valid
40
+ end
41
+ end
42
+
43
+ context 'when bound with no data' do
44
+ subject { ExampleForm.bind }
45
+
46
+ it { should be_bound }
47
+ end
48
+
49
+ context 'when valid' do
50
+ subject { ExampleForm.bind name: 'James', age: 26 }
51
+
52
+ it { should be_valid }
53
+
54
+ it 'has no errors' do
55
+ subject.valid?
56
+ subject.errors.should be_empty
57
+ end
58
+
59
+ it 'can be saved' do
60
+ subject.should_receive :persist!
61
+ subject.save!
62
+ end
63
+ end
64
+
65
+ context 'when invalid' do
66
+ let(:form) { ExampleForm.bind }
67
+
68
+ it 'has errors' do
69
+ subject.valid?
70
+ subject.errors.should be_present
71
+ end
72
+
73
+ it 'cannot be saved' do
74
+ expect { subject.save! }.to raise_error Cave::ValidationError
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,44 @@
1
+ require 'helper'
2
+
3
+ class Profile < OpenStruct
4
+ def create! attrs; end
5
+ def update_attributes attrs; end
6
+ end
7
+
8
+ class ExampleModelForm < Cave::ModelForm
9
+ model Profile
10
+
11
+ field :name, String
12
+ end
13
+
14
+ describe ExampleModelForm do
15
+
16
+ it 'checks instance against model' do
17
+ expect { ExampleModelForm.new 1 }.to raise_error TypeError
18
+ end
19
+
20
+ context 'when new' do
21
+ subject { ExampleModelForm.new name: 'James' }
22
+
23
+ it 'creates' do
24
+ Profile.should_receive :create!
25
+ subject.save!
26
+ end
27
+ end
28
+
29
+ context 'pre-existing' do
30
+ let(:instance) { Profile.new name: 'James' }
31
+ subject { ExampleModelForm.new instance, name: 'Jim' }
32
+
33
+ it 'updates' do
34
+ instance.should_receive(:update_attributes).with(name: 'Jim')
35
+ subject.save!
36
+ end
37
+
38
+ it 'does not create' do
39
+ Profile.should_not_receive :create!
40
+ subject.save!
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,2 @@
1
+ require 'rspec'
2
+ require 'cave'
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cave
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James Dabbs
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activemodel
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: virtus
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Cave Forms encapsulate validation logic into an object with that single
47
+ responsibility
48
+ email: jamesdabbs@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - Gemfile
54
+ - Rakefile
55
+ - README.md
56
+ - cave.gemspec
57
+ - lib/cave/errors.rb
58
+ - lib/cave/form.rb
59
+ - lib/cave/model_form.rb
60
+ - lib/cave.rb
61
+ - spec/gears/form_spec.rb
62
+ - spec/gears/model_form_spec.rb
63
+ - spec/helper.rb
64
+ homepage: http://github.com/jamesdabbs/cave
65
+ licenses: []
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 1.8.24
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: A simple Ruby form library, intended for use with Rails
88
+ test_files:
89
+ - spec/gears/form_spec.rb
90
+ - spec/gears/model_form_spec.rb
91
+ - spec/helper.rb