Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Actually support ActiveModel validations and callbacks

  • Loading branch information...
commit d97e5f19414e8886c8c5af92c2fcf3907724e899 1 parent e6a0f6d
@remiprev authored
View
25 README.md
@@ -350,7 +350,7 @@ However, validations must be triggered manually — they are not run, for exampl
class User
include Her::Model
- attr_accessor :fullname, :email
+ attributes :fullname, :email
validates :fullname, :presence => true
validates :email, :presence => true
end
@@ -362,6 +362,29 @@ end
# POST /users&fullname=Tobias+Fünke will still be called, even if the user is not valid
```
+### Dirty attributes
+
+Her includes `ActiveModel::Dirty` so you can keep track of the attributes that have changed in an object.
+an object, or `#create` on a model class.
+
+```ruby
+class User
+ include Her::Model
+
+ attributes :fullname, :email
+end
+
+@user = User.new(:fullname => "Tobias Fünke")
+@user.fullname_changed? # => true
+@user.changes # => { :fullname => [nil, "Tobias Fünke"] }
+
+@user.save
+# POST /users&fullname=Tobias+Fünke
+
+@user.fullname_changed? # => false
+@user.changes # => {}
+```
+
### Callbacks
You can add *before* and *after* callbacks to your models that are triggered on specific actions. You can use symbols or blocks.
View
33 lib/her/model/orm.rb
@@ -50,7 +50,8 @@ def self.use_setter_methods(model, params)
# @private
def method_missing(method, *args, &blk)
if method.to_s.end_with?('=')
- @data[method.to_s.chomp('=').to_sym] = args.first
+ attribute = method.to_s.chomp('=').to_sym
+ @data[attribute] = args.first
elsif method.to_s.end_with?('?')
@data.include?(method.to_s.chomp('?').to_sym)
elsif @data.include?(method)
@@ -65,6 +66,10 @@ def respond_to?(method, include_private = false)
method.to_s.end_with?('=') || method.to_s.end_with?('?') || @data.include?(method) || super
end
+ def respond_to_missing?(method, include_private = false)
+ method.to_s.end_with?('=') || method.to_s.end_with?('?') || @data.include?(method) || @data.include?(method) || super
+ end
+
# Assign new data to an instance
def assign_data(new_data)
new_data = Her::Model::ORM.use_setter_methods(self, new_data)
@@ -145,6 +150,7 @@ def save
update_data(self.class.parse(parsed_data[:data])) if parsed_data[:data].any?
self.metadata = parsed_data[:metadata]
self.response_errors = parsed_data[:errors]
+ self.changed_attributes.clear if self.changed_attributes.present?
return false if self.response_errors.any?
end
@@ -204,6 +210,30 @@ def new_collection(parsed_data)
Her::Model::ORM.initialize_collection(self, parsed_data)
end
+ # Define the attributes that will be used to track dirty attributes and validations
+ #
+ # @param [Array] attributes
+ def attributes(*attributes)
+ define_attribute_methods attributes
+
+ attributes.each do |attribute|
+ attribute = attribute.to_sym
+
+ define_method "#{attribute}".to_sym do
+ @data.include?(attribute) ? @data[attribute] : nil
+ end
+
+ define_method "#{attribute}=".to_sym do |value|
+ self.send("#{attribute}_will_change!".to_sym) if @data[attribute] != value
+ @data[attribute] = value
+ end
+
+ define_method "#{attribute}?".to_sym do
+ @data.include?(attribute)
+ end
+ end
+ end
+
# Parse data before assigning it to a resource
#
# @param [Hash] data
@@ -272,6 +302,7 @@ def create(params={})
update_data(data)
@metadata = parsed_data[:metadata]
@response_errors = parsed_data[:errors]
+ @changed_attributes.clear if @changed_attributes.present?
end
end
end
View
2  lib/her/version.rb
@@ -1,3 +1,3 @@
module Her
- VERSION = "0.5"
+ VERSION = "0.5.1"
end
View
59 spec/model/orm_spec.rb
@@ -464,6 +464,65 @@ def to_params
end
end
+ context "checking dirty attributes" do
+ before do
+ Her::API.setup :url => "https://api.example.com" do |builder|
+ builder.use Her::Middleware::FirstLevelParseJSON
+ builder.use Faraday::Request::UrlEncoded
+ builder.adapter :test do |stub|
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
+ stub.put("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
+ stub.post("/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
+ end
+ end
+
+ spawn_model "Foo::User" do
+ attributes :fullname, :email
+ end
+ end
+
+ context "for existing resource" do
+ it "tracks dirty attributes" do
+ user = Foo::User.find(1)
+ user.fullname = "Tobias Fünke"
+ user.fullname_changed?.should be_true
+ user.email_changed?.should be_false
+ user.should be_changed
+ user.save
+ user.should_not be_changed
+ end
+ end
+
+ context "for new resource" do
+ it "tracks dirty attributes" do
+ user = Foo::User.new
+ user.fullname = "Tobias Fünke"
+ user.fullname_changed?.should be_true
+ user.should be_changed
+ user.save
+ user.should_not be_changed
+ end
+ end
+ end
+
+ context "validating attributes" do
+ before do
+ spawn_model "Foo::User" do
+ attributes :fullname, :email
+ validates_presence_of :fullname
+ validates_presence_of :email
+ end
+ end
+
+ it "validates attributes when calling #valid?" do
+ user = Foo::User.new
+ user.should_not be_valid
+ user.fullname = "Tobias Fünke"
+ user.email = "tobias@bluthcompany.com"
+ user.should be_valid
+ end
+ end
+
context "when include_root_in_json is true" do
context "when include_root_in_json is true" do
before do
Please sign in to comment.
Something went wrong with that request. Please try again.