From 6dd695006336f3450ea9b267a6e0e33dffc49147 Mon Sep 17 00:00:00 2001 From: Andy Pike Date: Sat, 31 Jan 2015 22:11:19 +0000 Subject: [PATCH 1/3] Add Rails 4.2 compatibility I needed to add a few methods to allow this gem to work with Rails 4.2 and simple_form 3.1. These methods were as follows: Attribute#number? - returns true if the attribute type is numerical, otherwise returns false. Attribute#limit - returns the database column size limit which simple_form uses to set maxlength/size attributes on the input controls if there are no length validations. I return nil here so no html attributes are added. If you add length validations then these do still result in html attributes for length being added. SimpleFormObject#has_attribute? - returns true if the passed attribute has been declared for the form object otherwise returns false. In addition to these changes I have also added a new class method for over riding the model name used (for routing). Now you can do this: ``` class FooForm route_as :bar end ``` This will result in the form using the path helpers bars_path rather than foos_path etc. --- lib/simple_form_object.rb | 44 ++++++++---------------- lib/simple_form_object/attribute.rb | 39 ++++++++++++++++++++++ spec/attribute_spec.rb | 41 +++++++++++++++++++++++ spec/model_spec.rb | 52 ++++++++++++++++++++++++----- 4 files changed, 138 insertions(+), 38 deletions(-) create mode 100644 lib/simple_form_object/attribute.rb diff --git a/lib/simple_form_object.rb b/lib/simple_form_object.rb index 54eea04..4db8b5e 100644 --- a/lib/simple_form_object.rb +++ b/lib/simple_form_object.rb @@ -1,4 +1,5 @@ require "simple_form_object/version" +require "simple_form_object/attribute" require "active_model" require "active_support" @@ -14,6 +15,10 @@ def attribute(name, type = :string, options = {}) _attributes << Attribute.new(name, type, options) end + def route_as(model_name) + @model_name = model_name.to_s.camelize + end + def _attributes @_attributes ||= [] end @@ -23,7 +28,13 @@ def _attribute(attribute_name) end def model_name - ActiveModel::Name.new(self, nil, self.to_s.gsub(/Form$/, '')) + ActiveModel::Name.new(self, nil, model_name_for_routing) + end + + private + + def model_name_for_routing + @model_name || to_s.gsub(/Form$/, "") end end @@ -46,34 +57,7 @@ def attributes attribs end - class Attribute - def initialize(name, type = nil, options) - @name = name - @type = type || :string - @options = options - - extract_options - end - - attr_accessor :name, :type, :options - - def fake_column - self - end - - def apply_default_to(form) - if form.send(@name).nil? - form.send("#{@name}=", @default) if @apply_default - end - end - - private - - def extract_options - @apply_default = true - @default = options.fetch(:default) { @apply_default = false; nil } - @skip_validations = options.fetch(:skip_validations, false) - end + def has_attribute?(attribute) + attributes.key?(attribute) end - end diff --git a/lib/simple_form_object/attribute.rb b/lib/simple_form_object/attribute.rb new file mode 100644 index 0000000..f4ce5a7 --- /dev/null +++ b/lib/simple_form_object/attribute.rb @@ -0,0 +1,39 @@ +module SimpleFormObject + class Attribute + def initialize(name, type = nil, options) + @name = name + @type = type || :string + @options = options + + extract_options + end + + attr_accessor :name, :type, :options + + def fake_column + self + end + + def apply_default_to(form) + if form.send(@name).nil? + form.send("#{@name}=", @default) if @apply_default + end + end + + def number? + %i(integer float decimal).include?(type) + end + + def limit + nil + end + + private + + def extract_options + @apply_default = true + @default = options.fetch(:default) { @apply_default = false; nil } + @skip_validations = options.fetch(:skip_validations, false) + end + end +end diff --git a/spec/attribute_spec.rb b/spec/attribute_spec.rb index a4966bb..2434ad9 100644 --- a/spec/attribute_spec.rb +++ b/spec/attribute_spec.rb @@ -53,6 +53,47 @@ expect(form.foo).to eq false end end + end + + describe '#limit' do + subject(:attribute) { SimpleFormObject::Attribute.new(:name, :string, {}) } + + it 'returns nil' do + expect(subject.limit).to be_nil + end + end + + describe '#number?' do + context 'when attribute is an integer' do + subject(:attribute) { SimpleFormObject::Attribute.new(:name, :integer, {}) } + + it 'returns true' do + expect(subject).to be_number + end + end + + context 'when attribute is a float' do + subject(:attribute) { SimpleFormObject::Attribute.new(:name, :float, {}) } + it 'returns true' do + expect(subject).to be_number + end + end + + context 'when attribute is a decimal' do + subject(:attribute) { SimpleFormObject::Attribute.new(:name, :decimal, {}) } + + it 'returns true' do + expect(subject).to be_number + end + end + + context 'when attribute is a string' do + subject(:attribute) { SimpleFormObject::Attribute.new(:name, :string, {}) } + + it 'returns false' do + expect(subject).not_to be_number + end + end end end diff --git a/spec/model_spec.rb b/spec/model_spec.rb index 374c05a..8fd88e3 100644 --- a/spec/model_spec.rb +++ b/spec/model_spec.rb @@ -67,20 +67,56 @@ end describe '.model_name' do - let(:klass) do - class KlassForm - include SimpleFormObject + context 'by convention' do + let(:klass) do + class KlassForm + include SimpleFormObject + end + + KlassForm + end + + it 'should return an ActiveModel::Name' do + expect(klass.model_name).to be_a_kind_of ActiveModel::Name + end + + it 'should remove Form from the name of the class' do + expect(klass.model_name.name).to eq 'Klass' + end + end + + context 'by declaration' do + let(:klass) do + class KlassForm + include SimpleFormObject + + route_as :custom + end + + KlassForm + end + + it 'should return an ActiveModel::Name' do + expect(klass.model_name).to be_a_kind_of ActiveModel::Name + end + + it 'should remove Form from the name of the class' do + expect(klass.model_name.name).to eq 'Custom' end + end + end - KlassForm + describe '#has_attribute?' do + before do + klass.attribute :foo end - it 'should return an ActiveModel::Name' do - expect(klass.model_name).to be_a_kind_of ActiveModel::Name + it 'returns true for a declared attribute' do + expect(instance.has_attribute?(:foo)).to be(true) end - it 'should remove Form from the name of the class' do - expect(klass.model_name.name).to eq "Klass" + it 'returns false for a declared attribute' do + expect(instance.has_attribute?(:bar)).to be(false) end end end From e11d8e9ca4bb817fadda31269188ec4e7fbd6a68 Mon Sep 17 00:00:00 2001 From: Andy Pike Date: Sun, 7 Jun 2015 23:40:22 +0100 Subject: [PATCH 2/3] Add support for inheriting attributes --- lib/simple_form_object.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/simple_form_object.rb b/lib/simple_form_object.rb index 4db8b5e..0f4c8f4 100644 --- a/lib/simple_form_object.rb +++ b/lib/simple_form_object.rb @@ -20,7 +20,15 @@ def route_as(model_name) end def _attributes - @_attributes ||= [] + inherited_attributes = [] + + ancestors.drop(1).each do |ancestor| + if ancestor.respond_to?(:_attributes) + inherited_attributes.concat(ancestor._attributes) + end + end + + @_attributes ||= inherited_attributes end def _attribute(attribute_name) From 3e89489606c6cafd47f108a57f40ad89906800d1 Mon Sep 17 00:00:00 2001 From: Andy Pike Date: Mon, 16 Nov 2015 19:53:25 +0000 Subject: [PATCH 3/3] Allow attributes to have procs as default values --- lib/simple_form_object/attribute.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/simple_form_object/attribute.rb b/lib/simple_form_object/attribute.rb index f4ce5a7..382a02a 100644 --- a/lib/simple_form_object/attribute.rb +++ b/lib/simple_form_object/attribute.rb @@ -16,7 +16,8 @@ def fake_column def apply_default_to(form) if form.send(@name).nil? - form.send("#{@name}=", @default) if @apply_default + default_value = @default.respond_to?(:call) ? @default.call : @default + form.send("#{@name}=", default_value) if @apply_default end end