From 9299a793582b426d761b3d2c5738145f9faf3654 Mon Sep 17 00:00:00 2001 From: Brian Stien Date: Tue, 12 May 2015 13:45:20 -0600 Subject: [PATCH 1/3] Fix failing spec. --- spec/lib/active_remote/dirty_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/active_remote/dirty_spec.rb b/spec/lib/active_remote/dirty_spec.rb index faa773d..9810b6b 100644 --- a/spec/lib/active_remote/dirty_spec.rb +++ b/spec/lib/active_remote/dirty_spec.rb @@ -88,7 +88,7 @@ it "clears previous changes" do new_record = post.instantiate(record.to_hash) - new_record.previous_changes.should be_nil + new_record.previous_changes.should eq({}) end it "clears changes" do From 2e7bec7e79c24f2f3039c16c84149bcba8f4f97c Mon Sep 17 00:00:00 2001 From: Brian Stien Date: Tue, 12 May 2015 13:45:52 -0600 Subject: [PATCH 2/3] pry-nav is broken in jruby, use regular pry instead --- active_remote.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active_remote.gemspec b/active_remote.gemspec index ea8846c..448a57b 100644 --- a/active_remote.gemspec +++ b/active_remote.gemspec @@ -30,7 +30,7 @@ Gem::Specification.new do |s| s.add_development_dependency "rake" s.add_development_dependency "rspec" s.add_development_dependency "rspec-pride" - s.add_development_dependency "pry-nav" + s.add_development_dependency "pry" s.add_development_dependency "protobuf-rspec", ">= 1.0" s.add_development_dependency "simplecov" end From 87fb9eecef4ad20afe25ca6d07cf84dd3479ee5b Mon Sep 17 00:00:00 2001 From: Brian Stien Date: Tue, 12 May 2015 13:53:24 -0600 Subject: [PATCH 3/3] Add support for validations. --- lib/active_remote/base.rb | 5 ++ lib/active_remote/errors.rb | 11 ++++ lib/active_remote/persistence.rb | 12 ++-- lib/active_remote/validations.rb | 64 ++++++++++++++++++++++ spec/lib/active_remote/validations_spec.rb | 56 +++++++++++++++++++ spec/support/models/post.rb | 2 + 6 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 lib/active_remote/validations.rb create mode 100644 spec/lib/active_remote/validations_spec.rb diff --git a/lib/active_remote/base.rb b/lib/active_remote/base.rb index 4a6fb0c..d0aa7af 100644 --- a/lib/active_remote/base.rb +++ b/lib/active_remote/base.rb @@ -15,6 +15,7 @@ require 'active_remote/scope_keys' require 'active_remote/search' require 'active_remote/serialization' +require 'active_remote/validations' module ActiveRemote class Base @@ -39,6 +40,10 @@ class Base # so it needs to be included last. include Dirty + # Overrides persistence methods, so it must included after + include Validations + include ActiveModel::Validations::Callbacks + attr_reader :last_request, :last_response define_model_callbacks :initialize, :only => :after diff --git a/lib/active_remote/errors.rb b/lib/active_remote/errors.rb index d703d68..96b5283 100644 --- a/lib/active_remote/errors.rb +++ b/lib/active_remote/errors.rb @@ -9,6 +9,17 @@ class ActiveRemoteError < StandardError class ReadOnlyRemoteRecord < ActiveRemoteError end + # Raised by ActiveRemote::Validations when save is called on an invalid record. + class RemoteRecordInvalid < ActiveRemoteError + attr_reader :record + + def initialize(record) + @record = record + errors = @record.errors.full_messages.join(', ') + super(errors) + end + end + # Raised by ActiveRemove::Base.find when remote record is not found when # searching with the given arguments. class RemoteRecordNotFound < ActiveRemoteError diff --git a/lib/active_remote/persistence.rb b/lib/active_remote/persistence.rb index e70c4d7..85abb62 100644 --- a/lib/active_remote/persistence.rb +++ b/lib/active_remote/persistence.rb @@ -176,9 +176,9 @@ def readonly? # # Also runs any before/after save callbacks that are defined. # - def save + def save(*args) run_callbacks :save do - create_or_update + create_or_update(*args) end end @@ -192,8 +192,8 @@ def save # # Also runs any before/after save callbacks that are defined. # - def save! - save || raise(RemoteRecordNotSaved) + def save!(*args) + save(*args) || raise(RemoteRecordNotSaved) end # Returns true if the record doesn't have errors; otherwise, returns false. @@ -245,9 +245,9 @@ def create # are created, existing records are updated. If the record is marked as # readonly, an ActiveRemote::ReadOnlyRemoteRecord is raised. # - def create_or_update + def create_or_update(*args) raise ReadOnlyRemoteRecord if readonly? - new_record? ? create : update + new_record? ? create : update(*args) end # Handles updating a remote object and serializing it's attributes and diff --git a/lib/active_remote/validations.rb b/lib/active_remote/validations.rb new file mode 100644 index 0000000..443ee1c --- /dev/null +++ b/lib/active_remote/validations.rb @@ -0,0 +1,64 @@ +module ActiveRemote + module Validations + extend ActiveSupport::Concern + + # Attempts to save the record like Persistence, but will run + # validations and return false if the record is invalid + # + # Validations can be skipped by passing :validate => false + # + # example Save a record + # post.save + # + # example Save a record, skip validations + # post.save(:validate => false) + # + def save(options = {}) + perform_validations(options) ? super : false + end + + # Attempts to save the record like Persistence, but will raise + # ActiveRemote::RemoteRecordInvalid if the record is not valid + # + # Validations can be skipped by passing :validate => false + # + # example Save a record, raise and error if invalid + # post.save! + # + # example Save a record, skip validations + # post.save!(:validate => false) + # + def save!(options = {}) + perform_validations(options) ? super : raise_validation_error + end + + # Runs all the validations within the specified context. Returns true if + # no errors are found, false otherwise. + # + # Aliased as validate. + # + # example Is the record valid? + # post.valid? + # + # example Is the record valid for creation? + # post.valid?(:create) + # + def valid?(context = nil) + context ||= (new_record? ? :create : :update) + output = super(context) + errors.empty? && output + end + + alias_method :validate, :valid? + + protected + + def raise_validation_error + fail(::ActiveRemote::RemoteRecordInvalid.new(self)) + end + + def perform_validations(options = {}) + options[:validate] == false || valid?(options[:context]) + end + end +end diff --git a/spec/lib/active_remote/validations_spec.rb b/spec/lib/active_remote/validations_spec.rb new file mode 100644 index 0000000..b2be533 --- /dev/null +++ b/spec/lib/active_remote/validations_spec.rb @@ -0,0 +1,56 @@ +require 'spec_helper' + +describe ActiveRemote::Validations do + let(:invalid_record) { ::Post.new } + let(:valid_record) { ::Post.new(:name => 'test') } + + before { valid_record.stub(:create_or_update).and_return(true) } + before { invalid_record.stub(:create_or_update).and_return(true) } + + describe 'save' do + context 'valid record' do + it 'returns true' do + result = valid_record.save + result.should be true + end + end + + context 'invalid record' do + it 'returns false' do + result = invalid_record.save + result.should be false + end + end + end + + describe 'save!' do + context 'valid record' do + it 'returns true' do + result = valid_record.save! + result.should be true + end + end + + context 'invalid record' do + it 'raises invalid record error' do + expect { invalid_record.save! }.to raise_error(ActiveRemote::RemoteRecordInvalid) + end + end + end + + describe 'valid?' do + context 'valid record' do + it 'returns true' do + result = valid_record.valid? + result.should be true + end + end + + context 'invalid record' do + it 'returns false' do + result = invalid_record.valid? + result.should be false + end + end + end +end diff --git a/spec/support/models/post.rb b/spec/support/models/post.rb index a9cb951..7c95833 100644 --- a/spec/support/models/post.rb +++ b/spec/support/models/post.rb @@ -16,4 +16,6 @@ class Post < ::ActiveRemote::Base belongs_to :coauthor, :class_name => '::Author' belongs_to :bestseller, :class_name => '::Author', :foreign_key => :bestseller_guid belongs_to :user, :class_name => '::Author', :scope => :user_guid + + validates :name, :presence => true end