initial commit, yay super-easy form objects
This commit is contained in:
commit
d910e0e837
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
*.gem
|
||||||
|
*.rbc
|
||||||
|
.bundle
|
||||||
|
.config
|
||||||
|
.yardoc
|
||||||
|
Gemfile.lock
|
||||||
|
InstalledFiles
|
||||||
|
_yardoc
|
||||||
|
coverage
|
||||||
|
doc/
|
||||||
|
lib/bundler/man
|
||||||
|
pkg
|
||||||
|
rdoc
|
||||||
|
spec/reports
|
||||||
|
test/tmp
|
||||||
|
test/version_tmp
|
||||||
|
tmp
|
4
Gemfile
Normal file
4
Gemfile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
# Specify your gem's dependencies in carapace.gemspec
|
||||||
|
gemspec
|
22
LICENSE.txt
Normal file
22
LICENSE.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2013 John Bintz
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
51
README.md
Normal file
51
README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# CANDY WRAPPER
|
||||||
|
|
||||||
|
Use form objects with ease. Plugs into `inherited_resources` easily:
|
||||||
|
|
||||||
|
``` ruby
|
||||||
|
# app/models/database_object.rb
|
||||||
|
class DatabaseObject < Persistence::Base
|
||||||
|
# ... persistence and relationships only ...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
``` ruby
|
||||||
|
# app/controllers/database_objects_controller.rb
|
||||||
|
|
||||||
|
class DatabaseObjectsController < ApplicationController
|
||||||
|
inherit_resources
|
||||||
|
|
||||||
|
# for the parts of inherited_resources that actually persist models,
|
||||||
|
# ensure that persistence takes placed within a CandyWrapper::ModelWrapper
|
||||||
|
# form object. Those respond to save, assign_attributes, and update_attributes.
|
||||||
|
wrap_in_form_object!
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
``` ruby
|
||||||
|
# app/form_objects/database_object_form_object.rb
|
||||||
|
|
||||||
|
class DatabaseObjectFormObject < CandyWrapper::ModelWrapper
|
||||||
|
def complex_parameter=(database_object, parameter_value)
|
||||||
|
# do complex formatting here, probably for nested objects
|
||||||
|
# database_object will be saved by this point
|
||||||
|
end
|
||||||
|
|
||||||
|
# perform these actions before database_object is saved
|
||||||
|
before_wrapped_save :process_first_parameter
|
||||||
|
|
||||||
|
def process_first_parameter=(database_object, parameter_value)
|
||||||
|
# use normal accessors to set properties on database_object, it will be
|
||||||
|
# saved when all before_wrapped_saved methods are run
|
||||||
|
end
|
||||||
|
|
||||||
|
# there is no guarantee in the order that these will run, don't make them
|
||||||
|
# depend on each other!
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coming soon!
|
||||||
|
|
||||||
|
* Form objects that hang off of models as if they were relationships
|
||||||
|
* A better readme!
|
||||||
|
|
26
candy_wrapper.gemspec
Normal file
26
candy_wrapper.gemspec
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
lib = File.expand_path('../lib', __FILE__)
|
||||||
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||||
|
require 'candy_wrapper/version'
|
||||||
|
|
||||||
|
Gem::Specification.new do |spec|
|
||||||
|
spec.name = "candy_wrapper"
|
||||||
|
spec.version = CandyWrapper::VERSION
|
||||||
|
spec.authors = ["John Bintz"]
|
||||||
|
spec.email = ["john@coswellproductions.com"]
|
||||||
|
spec.description = %q{Use form objects with ease.}
|
||||||
|
spec.summary = %q{Use form objects with ease.}
|
||||||
|
spec.homepage = ""
|
||||||
|
spec.license = "MIT"
|
||||||
|
|
||||||
|
spec.files = `git ls-files`.split($/)
|
||||||
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
||||||
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
||||||
|
spec.require_paths = ["lib"]
|
||||||
|
|
||||||
|
spec.add_development_dependency "bundler", "~> 1.3"
|
||||||
|
spec.add_development_dependency "rake"
|
||||||
|
spec.add_development_dependency "rspec"
|
||||||
|
spec.add_dependency 'activesupport'
|
||||||
|
end
|
||||||
|
|
4
lib/candy_wrapper.rb
Normal file
4
lib/candy_wrapper.rb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
require "candy_wrapper/version"
|
||||||
|
require "candy_wrapper/model_wrapper"
|
||||||
|
require "candy_wrapper/inherited_resources"
|
||||||
|
|
59
lib/candy_wrapper/inherited_resources.rb
Normal file
59
lib/candy_wrapper/inherited_resources.rb
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
require 'active_support/concern'
|
||||||
|
|
||||||
|
module CandyWrapper
|
||||||
|
module InheritedResources
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def __candy_wrapper__
|
||||||
|
@__candy_wrapper__
|
||||||
|
end
|
||||||
|
|
||||||
|
def wrap_in_form_object(options = {})
|
||||||
|
@__candy_wrapper__ = {
|
||||||
|
with: resource_class.name + "FormObject",
|
||||||
|
only: [ :create, :update ]
|
||||||
|
}.merge(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def wrap_in_form_object!(*args)
|
||||||
|
wrap_in_form_object(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
def build_resource
|
||||||
|
get_resource_ivar || set_resource_ivar(
|
||||||
|
if self.class.__candy_wrapper__[:only].include?(action_name.to_sym)
|
||||||
|
object = end_of_association_chain.send(method_for_build)
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_resource(object)
|
||||||
|
if self.class.__candy_wrapper__[:only].include?(action_name.to_sym)
|
||||||
|
wrapped_object = candy_wrapper_class.new(object, *resource_params)
|
||||||
|
|
||||||
|
wrapped_object.save
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_resource(object, attributes)
|
||||||
|
if self.class.__candy_wrapper__[:only].include?(action_name.to_sym)
|
||||||
|
wrapped_object = candy_wrapper_class.new(object)
|
||||||
|
wrapped_object.update_attributes(*attributes)
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def candy_wrapper_class
|
||||||
|
@__candy_wrapper_class__ ||= self.class.__candy_wrapper__[:with].constantize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
94
lib/candy_wrapper/model_wrapper.rb
Normal file
94
lib/candy_wrapper/model_wrapper.rb
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
require 'delegate'
|
||||||
|
|
||||||
|
module CandyWrapper
|
||||||
|
class ModelWrapper < SimpleDelegator
|
||||||
|
def self.inherited(klass)
|
||||||
|
klass.send(:extend, ClassMethods)
|
||||||
|
|
||||||
|
if klass.name && original = klass.name[/^(.*)FormObject$/, 1]
|
||||||
|
klass.wraps original.constantize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def wraps(klass = nil)
|
||||||
|
if klass
|
||||||
|
@__wraps__ = klass
|
||||||
|
else
|
||||||
|
@__wraps__
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def before_wrapped_save(*args)
|
||||||
|
@__before_wrapped_save__ ||= []
|
||||||
|
|
||||||
|
if args.empty?
|
||||||
|
@__before_wrapped_save__
|
||||||
|
else
|
||||||
|
@__before_wrapped_save__ += args
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def __wraps__
|
||||||
|
self.class.wraps
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(object, params = {})
|
||||||
|
@__object__ = object
|
||||||
|
|
||||||
|
assign_attributes(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def __getobj__
|
||||||
|
@__object__
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign_attributes(attributes)
|
||||||
|
@__params__ = attributes.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_attributes(attributes)
|
||||||
|
assign_attributes(attributes)
|
||||||
|
|
||||||
|
save
|
||||||
|
end
|
||||||
|
|
||||||
|
def save
|
||||||
|
self.class.before_wrapped_save.each do |before|
|
||||||
|
send("#{before}=", @__object__, @__params__[before])
|
||||||
|
end
|
||||||
|
|
||||||
|
@__object__.assign_attributes(__object_params__)
|
||||||
|
|
||||||
|
if result = @__object__.save
|
||||||
|
setters_and_setter_params.each do |setter, setter_param|
|
||||||
|
send(setter, @__object__, @__params__[setter_param])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def __object_params__
|
||||||
|
object_params = @__params__.dup
|
||||||
|
setter_params.each { |key| object_params.delete(key) }
|
||||||
|
object_params
|
||||||
|
end
|
||||||
|
|
||||||
|
def setter_params
|
||||||
|
@__setter_params__ ||= setters.collect { |param| param.gsub('=', '').to_sym }
|
||||||
|
end
|
||||||
|
|
||||||
|
def setters
|
||||||
|
@__setters__ ||= (my_instance_methods.collect(&:to_s).find_all { |param| param[/=\Z/] } - self.class.before_wrapped_save)
|
||||||
|
end
|
||||||
|
|
||||||
|
def setters_and_setter_params
|
||||||
|
setters.zip(setter_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def my_instance_methods
|
||||||
|
@my_instance_methods ||= (self.class.instance_methods - ModelWrapper.instance_methods)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
3
lib/candy_wrapper/version.rb
Normal file
3
lib/candy_wrapper/version.rb
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module CandyWrapper
|
||||||
|
VERSION = "0.0.1"
|
||||||
|
end
|
51
spec/candy_wrapper/model_wrapper_spec.rb
Normal file
51
spec/candy_wrapper/model_wrapper_spec.rb
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
require 'candy_wrapper/model_wrapper'
|
||||||
|
|
||||||
|
describe CandyWrapper::ModelWrapper do
|
||||||
|
describe '#update_attributes' do
|
||||||
|
let(:model_class) {
|
||||||
|
Class.new do
|
||||||
|
attr_reader :saved, :attributes
|
||||||
|
|
||||||
|
def save
|
||||||
|
@saved = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign_attributes(attributes)
|
||||||
|
@attributes = attributes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:wrap_class) {
|
||||||
|
klass = Class.new(CandyWrapper::ModelWrapper) do
|
||||||
|
attr_reader :resource, :value
|
||||||
|
|
||||||
|
def setter=(resource, value)
|
||||||
|
@resource, @value = resource, value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
klass.send(:wraps, model_class)
|
||||||
|
klass
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:setter_value) { 'setter value' }
|
||||||
|
let(:base_attributes) { { :base => 'base' } }
|
||||||
|
let(:attributes) { { :setter => setter_value }.merge(base_attributes) }
|
||||||
|
|
||||||
|
it 'should save the model and trigger the setters on the wrapper' do
|
||||||
|
model = model_class.new
|
||||||
|
wrap = wrap_class.new(model)
|
||||||
|
|
||||||
|
wrap.update_attributes(attributes)
|
||||||
|
|
||||||
|
model.saved.should be_true
|
||||||
|
model.attributes.should be == base_attributes
|
||||||
|
|
||||||
|
wrap.resource.should == model
|
||||||
|
wrap.value.should == setter_value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
4
spec/spec_helper.rb
Normal file
4
spec/spec_helper.rb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
require 'rspec'
|
||||||
|
|
||||||
|
$: << File.expand_path('../../lib')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user