active_data 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. data/lib/active_data/attributes/base.rb +64 -0
  2. data/lib/active_data/attributes/localized.rb +42 -0
  3. data/lib/active_data/model/associations/many.rb +24 -0
  4. data/lib/active_data/model/associations.rb +49 -0
  5. data/lib/active_data/model/attributable.rb +42 -43
  6. data/lib/active_data/model/collectionizable/proxy.rb +1 -1
  7. data/lib/active_data/model/collectionizable.rb +1 -2
  8. data/lib/active_data/model/extensions/array.rb +1 -5
  9. data/lib/active_data/model/extensions/big_decimal.rb +17 -0
  10. data/lib/active_data/model/extensions/boolean.rb +20 -12
  11. data/lib/active_data/model/extensions/date.rb +1 -1
  12. data/lib/active_data/model/extensions/float.rb +17 -0
  13. data/lib/active_data/model/extensions/hash.rb +1 -5
  14. data/lib/active_data/model/extensions/integer.rb +2 -2
  15. data/lib/active_data/model/extensions/localized.rb +22 -0
  16. data/lib/active_data/model/extensions/string.rb +1 -1
  17. data/lib/active_data/model/extensions.rb +4 -0
  18. data/lib/active_data/model/localizable.rb +31 -0
  19. data/lib/active_data/model/nested_attributes.rb +52 -0
  20. data/lib/active_data/model/parameterizable.rb +29 -0
  21. data/lib/active_data/model.rb +16 -4
  22. data/lib/active_data/version.rb +1 -1
  23. data/spec/lib/active_data/model/associations_spec.rb +35 -0
  24. data/spec/lib/active_data/model/attributable_spec.rb +15 -13
  25. data/spec/lib/active_data/model/attributes/localized_spec.rb +48 -0
  26. data/spec/lib/active_data/model/nested_attributes_spec.rb +46 -0
  27. data/spec/lib/active_data/model/{serializable_spec.rb → type_cast_spec.rb} +29 -1
  28. data/spec/lib/active_data/model_spec.rb +3 -3
  29. metadata +26 -5
  30. data/lib/active_data/model/serializable.rb +0 -18
@@ -0,0 +1,64 @@
1
+ module ActiveData
2
+ module Attributes
3
+ class Base
4
+ attr_reader :name, :options
5
+
6
+ def initialize name, options = {}, &block
7
+ @name = name.to_sym
8
+ @options = options
9
+ @options[:default] = block if block
10
+ end
11
+
12
+ def type
13
+ @type ||= options[:type] || String
14
+ end
15
+
16
+ def values
17
+ @values ||= options[:in].dup if options[:in]
18
+ end
19
+
20
+ def default
21
+ @default ||= options[:default].respond_to?(:call) ? options[:default] : proc { options[:default] }
22
+ end
23
+
24
+ def default_value instance
25
+ default.call instance
26
+ end
27
+
28
+ def type_cast value
29
+ type.active_data_type_cast value
30
+ end
31
+
32
+ def generate_instance_methods context
33
+ context.class_eval <<-EOS
34
+ def #{name}
35
+ read_attribute(:#{name})
36
+ end
37
+
38
+ def #{name}= value
39
+ write_attribute(:#{name}, value)
40
+ end
41
+
42
+ def #{name}?
43
+ read_attribute(:#{name}).present?
44
+ end
45
+
46
+ def #{name}_before_type_cast
47
+ read_attribute_before_type_cast(:#{name})
48
+ end
49
+ EOS
50
+ end
51
+
52
+ def generate_singleton_methods context
53
+ if values
54
+ context.class_eval <<-EOS
55
+ def #{name}_values
56
+ _attributes[:#{name}].values
57
+ end
58
+ EOS
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,42 @@
1
+ module ActiveData
2
+ module Attributes
3
+ class Localized < Base
4
+
5
+ def default_value *args
6
+ {}
7
+ end
8
+
9
+ def generate_instance_methods context
10
+ context.class_eval <<-EOS
11
+ def #{name}_translations
12
+ read_attribute(:#{name})
13
+ end
14
+
15
+ def #{name}_translations= value
16
+ write_attribute(:#{name}, value)
17
+ end
18
+
19
+ def #{name}
20
+ read_localized_attribute(:#{name})
21
+ end
22
+
23
+ def #{name}= value
24
+ write_localized_attribute(:#{name}, value)
25
+ end
26
+
27
+ def #{name}?
28
+ read_localized_attribute(:#{name}).present?
29
+ end
30
+
31
+ def #{name}_before_type_cast
32
+ read_localized_attribute_before_type_cast(:#{name})
33
+ end
34
+ EOS
35
+ end
36
+
37
+ def generate_singleton_methods context
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ class Many
5
+ attr_reader :name, :klass, :options
6
+
7
+ def initialize name, options = {}
8
+ @name, @options = name.to_s, options
9
+ @klass ||= options[:class] || (options[:class_name].to_s.presence || name.to_s.classify).safe_constantize
10
+ raise "Can not determine class for `#{name}` association" unless @klass
11
+ end
12
+
13
+ def class_name
14
+ klass.to_s
15
+ end
16
+
17
+ def collection?
18
+ true
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,49 @@
1
+ require 'active_data/model/associations/many'
2
+
3
+ module ActiveData
4
+ module Model
5
+ module Associations
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_associations, :instance_reader => false, :instance_writer => false
10
+ self._associations = ActiveSupport::HashWithIndifferentAccess.new
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+ def reflect_on_association name
16
+ _associations[name]
17
+ end
18
+
19
+ def associations
20
+ _associations
21
+ end
22
+
23
+ def association_names
24
+ _associations.keys
25
+ end
26
+
27
+ def embeds_many name, options = {}
28
+ association = Many.new name, options
29
+ define_collection_reader association
30
+ define_collection_writer association
31
+ self._associations = _associations.merge!(association.name => association)
32
+ end
33
+
34
+ def define_collection_reader association
35
+ define_method association.name do
36
+ instance_variable_get("@#{association.name}") || association.klass.collection([])
37
+ end
38
+ end
39
+
40
+ def define_collection_writer association
41
+ define_method "#{association.name}=" do |value|
42
+ instance_variable_set "@#{association.name}", association.klass.collection(value)
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,61 +1,44 @@
1
- require 'active_data/model/serializable'
2
-
3
1
  module ActiveData
4
2
  module Model
5
3
  module Attributable
6
- include Serializable
7
4
  extend ActiveSupport::Concern
8
5
 
9
6
  included do
10
7
  class_attribute :_attributes, :instance_reader => false, :instance_writer => false
11
8
  self._attributes = ActiveSupport::HashWithIndifferentAccess.new
12
9
 
10
+ extend generated_class_attributes_methods
11
+ include generated_instance_attributes_methods
12
+
13
13
  delegate :attribute_default, :to => 'self.class'
14
14
  end
15
15
 
16
16
  module ClassMethods
17
- def attribute name, options = {}, &block
18
- default = options.is_a?(Hash) ? options[:default] : options
19
- type = options.is_a?(Hash) ? normalize_type(options[:type]) : String
20
- self._attributes = self._attributes.merge(name => {
21
- default: (block || default),
22
- type: type
23
- })
24
-
25
- define_method name do
26
- read_attribute(name)
27
- end
28
- define_method "#{name}_before_type_cast" do
29
- read_attribute_before_type_cast(name)
30
- end
31
- define_method "#{name}?" do
32
- read_attribute(name).present?
33
- end
34
- define_method "#{name}=" do |value|
35
- write_attribute(name, value)
17
+ def build_attribute name, options = {}, &block
18
+ klass = case options[:type].to_s
19
+ when 'Localized'
20
+ ActiveData::Attributes::Localized
21
+ else
22
+ ActiveData::Attributes::Base
36
23
  end
24
+ klass.new name, options, &block
25
+ end
37
26
 
38
- if options.is_a?(Hash) && options[:in]
39
- define_singleton_method "#{name}_values" do
40
- options[:in].dup
41
- end
42
- end
27
+ def attribute name, options = {}, &block
28
+ attribute = build_attribute(name, options, &block)
29
+ self._attributes = _attributes.merge(attribute.name => attribute)
30
+
31
+ attribute.generate_instance_methods generated_instance_attributes_methods
32
+ attribute.generate_singleton_methods generated_class_attributes_methods
33
+ attribute
43
34
  end
44
35
 
45
- def normalize_type type
46
- case type
47
- when String, Symbol then
48
- type.to_s.camelize.safe_constantize
49
- when nil then
50
- String
51
- else
52
- type
53
- end
36
+ def generated_class_attributes_methods
37
+ @generated_class_attributes_methods ||= Module.new
54
38
  end
55
39
 
56
- def attribute_default name
57
- default = _attributes[name][:default]
58
- default.respond_to?(:call) ? default.call : default
40
+ def generated_instance_attributes_methods
41
+ @generated_instance_attributes_methods ||= Module.new
59
42
  end
60
43
 
61
44
  def initialize_attributes
@@ -67,17 +50,22 @@ module ActiveData
67
50
  end
68
51
 
69
52
  def read_attribute name
70
- @attributes[name].nil? ? attribute_default(name) : @attributes[name]
53
+ attribute = self.class._attributes[name]
54
+ source = @attributes[name].nil? ? attribute.default_value(self) : @attributes[name]
55
+ attribute.type_cast source
71
56
  end
72
57
  alias_method :[], :read_attribute
73
58
 
59
+ def has_attribute? name
60
+ @attributes.key? name
61
+ end
62
+
74
63
  def read_attribute_before_type_cast name
75
- deserialize(send(name))
64
+ @attributes[name]
76
65
  end
77
66
 
78
67
  def write_attribute name, value
79
- type = self.class._attributes[name][:type]
80
- @attributes[name] = serialize(value, type)
68
+ @attributes[name] = value
81
69
  end
82
70
  alias_method :[]=, :write_attribute
83
71
 
@@ -104,6 +92,10 @@ module ActiveData
104
92
  self.attributes = attributes
105
93
  end
106
94
 
95
+ def reverse_update_attributes attributes
96
+ reverse_assign_attributes(attributes)
97
+ end
98
+
107
99
  private
108
100
 
109
101
  def assign_attributes attributes
@@ -113,6 +105,13 @@ module ActiveData
113
105
  self.attributes
114
106
  end
115
107
 
108
+ def reverse_assign_attributes attributes
109
+ (attributes.presence || {}).each do |(name, value)|
110
+ send("#{name}=", value) if respond_to?("#{name}=") && respond_to?(name) && send(name).blank?
111
+ end
112
+ self.attributes
113
+ end
114
+
116
115
  end
117
116
  end
118
117
  end
@@ -32,7 +32,7 @@ module ActiveData
32
32
  end
33
33
 
34
34
  module ClassMethods
35
- def modelize value
35
+ def active_data_type_cast value
36
36
  new value
37
37
  end
38
38
  end
@@ -6,13 +6,12 @@ module ActiveData
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- collectionize
9
+ collectionize Array
10
10
  end
11
11
 
12
12
  module ClassMethods
13
13
 
14
14
  def collectionize collection_superclass = nil
15
- collection_superclass ||= Array
16
15
  collection = Class.new(collection_superclass) do
17
16
  include ActiveData::Model::Collectionizable::Proxy
18
17
  end
@@ -4,12 +4,8 @@ module ActiveData
4
4
  module Array
5
5
  extend ActiveSupport::Concern
6
6
 
7
- def demodelize
8
- join(', ')
9
- end
10
-
11
7
  module ClassMethods
12
- def modelize value
8
+ def active_data_type_cast value
13
9
  case value
14
10
  when String then
15
11
  value.split(',').map(&:strip)
@@ -0,0 +1,17 @@
1
+ module ActiveData
2
+ module Model
3
+ module Extensions
4
+ module BigDecimal
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def active_data_type_cast value
9
+ ::BigDecimal.new value.to_s if value.to_s =~ /\A\d+(?:\.\d*)?\Z/
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ BigDecimal.send :include, ActiveData::Model::Extensions::BigDecimal
@@ -5,21 +5,29 @@ module ActiveData
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  MAPPING = {
8
- "1" => true,
9
- "0" => false,
10
- "t" => true,
11
- "f" => false,
12
- "T" => true,
13
- "F" => false,
14
- "true" => true,
15
- "false" => false,
16
- "TRUE" => true,
17
- "FALSE" => false
8
+ 1 => true,
9
+ 0 => false,
10
+ '1' => true,
11
+ '0' => false,
12
+ 't' => true,
13
+ 'f' => false,
14
+ 'T' => true,
15
+ 'F' => false,
16
+ true => true,
17
+ false => false,
18
+ 'true' => true,
19
+ 'false' => false,
20
+ 'TRUE' => true,
21
+ 'FALSE' => false,
22
+ 'y' => true,
23
+ 'n' => false,
24
+ 'yes' => true,
25
+ 'no' => false,
18
26
  }
19
27
 
20
28
  module ClassMethods
21
- def modelize value
22
- MAPPING[value.to_s]
29
+ def active_data_type_cast value
30
+ MAPPING[value]
23
31
  end
24
32
  end
25
33
  end
@@ -5,7 +5,7 @@ module ActiveData
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  module ClassMethods
8
- def modelize value
8
+ def active_data_type_cast value
9
9
  case value
10
10
  when String then
11
11
  Date.parse(value.to_s) rescue nil
@@ -0,0 +1,17 @@
1
+ module ActiveData
2
+ module Model
3
+ module Extensions
4
+ module Float
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def active_data_type_cast value
9
+ value.try(:to_f) if value.to_s =~ /\A\d+(?:\.\d*)?\Z/
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ Float.send :include, ActiveData::Model::Extensions::Float
@@ -4,12 +4,8 @@ module ActiveData
4
4
  module Hash
5
5
  extend ActiveSupport::Concern
6
6
 
7
- def demodelize
8
- nil
9
- end
10
-
11
7
  module ClassMethods
12
- def modelize value
8
+ def active_data_type_cast value
13
9
  case value
14
10
  when Hash then
15
11
  value
@@ -5,8 +5,8 @@ module ActiveData
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  module ClassMethods
8
- def modelize value
9
- value.try(:to_i) if value.to_s =~ /\A\d+\Z/
8
+ def active_data_type_cast value
9
+ value.try(:to_i) if value.to_s =~ /\A\d+(?:\.\d*)?\Z/
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,22 @@
1
+ module ActiveData
2
+ module Model
3
+ module Extensions
4
+ module Localized
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def active_data_type_cast value
9
+ case value
10
+ when Hash then
11
+ value.stringify_keys!
12
+ else
13
+ nil
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ Localized.send :include, ActiveData::Model::Extensions::Localized
@@ -5,7 +5,7 @@ module ActiveData
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  module ClassMethods
8
- def modelize value
8
+ def active_data_type_cast value
9
9
  value.to_s if value.present?
10
10
  end
11
11
  end
@@ -2,4 +2,8 @@ unless defined?(Boolean)
2
2
  class Boolean; end
3
3
  end
4
4
 
5
+ unless defined?(Localized)
6
+ class Localized; end
7
+ end
8
+
5
9
  Dir["#{File.dirname(__FILE__)}/extensions/*.rb"].each { |f| require f }
@@ -0,0 +1,31 @@
1
+ module ActiveData
2
+ module Model
3
+ module Localizable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ end
8
+
9
+ module ClassMethods
10
+ def fallbacks locale
11
+ ::I18n.respond_to?(:fallbacks) ? ::I18n.fallbacks[locale] : [locale]
12
+ end
13
+
14
+ def locale
15
+ I18n.locale
16
+ end
17
+ end
18
+
19
+ def read_localized_attribute name, locale = self.class.locale
20
+ translations = read_attribute(name)
21
+ translations[self.class.fallbacks(locale).detect { |fallback| translations[fallback.to_s] }]
22
+ end
23
+ alias_method :read_localized_attribute_before_type_cast, :read_localized_attribute
24
+
25
+ def write_localized_attribute name, value, locale = self.class.locale
26
+ translations = read_attribute(name)
27
+ write_attribute(name, translations.merge(locale.to_s => value))
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ module ActiveData
2
+ module Model
3
+ module NestedAttributes
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+
8
+ def nested_attributes? association_name
9
+ method_defined?(:"#{association_name}_attributes=")
10
+ end
11
+
12
+ def accepts_nested_attributes_for *attr_names
13
+ attr_names.each do |association_name|
14
+ reflection = reflect_on_association association_name
15
+ type = (reflection.collection? ? :collection : :one_to_one)
16
+
17
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
18
+ if method_defined?(:#{association_name}_attributes=)
19
+ remove_method(:#{association_name}_attributes=)
20
+ end
21
+ def #{association_name}_attributes=(attributes)
22
+ assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
23
+ end
24
+ EOS
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ def assign_nested_attributes_for_collection_association(association_name, attributes_collection, assignment_opts = {})
31
+ unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
32
+ raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
33
+ end
34
+
35
+ if attributes_collection.is_a? Hash
36
+ keys = attributes_collection.keys
37
+ attributes_collection = if keys.include?('id') || keys.include?(:id)
38
+ Array.wrap(attributes_collection)
39
+ else
40
+ attributes_collection.values
41
+ end
42
+ end
43
+
44
+ reflection = self.class.reflect_on_association association_name
45
+
46
+ send("#{association_name}=", attributes_collection.map do |attributes|
47
+ reflection.klass.new attributes
48
+ end)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ module ActiveData
2
+ module Model
3
+ module Parameterizable
4
+ extend ActiveSupport::Concern
5
+
6
+ def to_params options = nil
7
+ hash = serializable_hash options
8
+
9
+ self.class.association_names.each do |association_name|
10
+ if self.class.nested_attributes? association_name
11
+ records = send(association_name)
12
+ hash["#{association_name}_attributes"] = if records.is_a?(Enumerable)
13
+ attributes = {}
14
+ records.each_with_index do |a, i|
15
+ key = a.has_attribute?(:id) && a.id? ? a.id : i
16
+ attributes[key.to_s] = a.serializable_hash
17
+ end
18
+ attributes
19
+ else
20
+ records.serializable_hash
21
+ end
22
+ end
23
+ end
24
+
25
+ hash
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,9 +1,18 @@
1
+ require 'active_data/model/extensions'
1
2
  require 'active_data/model/collectionizable'
2
3
  require 'active_data/model/attributable'
3
- require 'active_data/model/extensions'
4
+ require 'active_data/model/localizable'
5
+ require 'active_data/model/associations'
6
+ require 'active_data/model/nested_attributes'
7
+ require 'active_data/model/parameterizable'
8
+
9
+ require 'active_data/attributes/base'
10
+ require 'active_data/attributes/localized'
4
11
 
5
12
  module ActiveData
6
13
  module Model
14
+ class NotFound < ::StandardError
15
+ end
7
16
 
8
17
  extend ActiveSupport::Concern
9
18
 
@@ -17,7 +26,11 @@ module ActiveData
17
26
  include ActiveModel::Serializers::Xml
18
27
 
19
28
  include Attributable
29
+ include Localizable
20
30
  include Collectionizable
31
+ include Associations
32
+ include NestedAttributes
33
+ include Parameterizable
21
34
  extend ActiveModel::Callbacks
22
35
  extend ActiveModel::Naming
23
36
  extend ActiveModel::Translation
@@ -36,8 +49,7 @@ module ActiveData
36
49
  end
37
50
 
38
51
  module ClassMethods
39
- def instantiate attributes = nil
40
- attributes ||= {}
52
+ def instantiate attributes
41
53
  return attributes if attributes.instance_of? self
42
54
 
43
55
  instance = allocate
@@ -67,4 +79,4 @@ module ActiveData
67
79
  end
68
80
 
69
81
  end
70
- end
82
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveData
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,35 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe ActiveData::Model::Associations do
5
+
6
+ class Assoc
7
+ include ActiveData::Model
8
+
9
+ attribute :name
10
+ end
11
+
12
+ let(:klass) do
13
+ Class.new do
14
+ include ActiveData::Model
15
+
16
+ attribute :name
17
+ embeds_many :assocs
18
+ end
19
+ end
20
+
21
+ let(:instance){klass.new(:name => 'world')}
22
+
23
+ context do
24
+ specify { instance.assocs.should be_empty }
25
+ specify { instance.assocs.should be_instance_of Assoc::Collection }
26
+ specify { instance.assocs.count.should == 0 }
27
+ end
28
+
29
+ context 'accessor' do
30
+ before { instance.assocs = [Assoc.new(:name => 'foo'), Assoc.new(:name => 'bar')] }
31
+ specify { instance.assocs.count.should == 2 }
32
+ specify { instance.assocs[0].name.should == 'foo' }
33
+ specify { instance.assocs[1].name.should == 'bar' }
34
+ end
35
+ end
@@ -9,8 +9,9 @@ describe ActiveData::Model::Attributable do
9
9
  attr_reader :name
10
10
 
11
11
  attribute :hello
12
- attribute :count, type: :integer, default: 10
13
- attribute(:calc, type: :integer) {2 + 3}
12
+ attribute :count, type: Integer, default: 10
13
+ attribute(:calc, type: Integer) { 2 + 3 }
14
+ attribute :enum, type: Integer, in: [1, 2, 3]
14
15
 
15
16
  def initialize name = nil
16
17
  @attributes = self.class.initialize_attributes
@@ -20,16 +21,17 @@ describe ActiveData::Model::Attributable do
20
21
  end
21
22
 
22
23
  context do
23
- subject{klass.new('world')}
24
- its(:attributes){should == {"hello"=>nil, "count"=>10, "calc"=>5}}
25
- its(:present_attributes){should == {"count"=>10, "calc"=>5}}
26
- its(:name){should == 'world'}
27
- its(:hello){should be_nil}
28
- its(:count){should == 10}
29
- its(:calc){should == 5}
30
- specify{expect{subject.hello = 'worlds'}.to change{subject.hello}.from(nil).to('worlds')}
31
- specify{expect{subject.count = 20}.to change{subject.count}.from(10).to(20)}
32
- specify{expect{subject.calc = 15}.to change{subject.calc}.from(5).to(15)}
24
+ subject { klass.new('world') }
25
+ specify { klass.enum_values == [1, 2, 3] }
26
+ its(:attributes) { should == { "hello"=>nil, "count"=>10, "calc"=>5, "enum" => nil } }
27
+ its(:present_attributes) { should == { "count"=>10, "calc"=>5 } }
28
+ its(:name) { should == 'world' }
29
+ its(:hello) { should be_nil }
30
+ its(:count) { should == 10 }
31
+ its(:calc) { should == 5 }
32
+ specify { expect { subject.hello = 'worlds' } .to change { subject.hello } .from(nil).to('worlds') }
33
+ specify { expect { subject.count = 20 } .to change { subject.count } .from(10).to(20) }
34
+ specify { expect { subject.calc = 15 } .to change { subject.calc } .from(5).to(15) }
33
35
  end
34
36
 
35
- end
37
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Localized do
5
+ let(:klass) do
6
+ Class.new do
7
+ include ActiveData::Model
8
+
9
+ attribute :name, type: Localized
10
+ end
11
+ end
12
+ let(:translations) { { en: 'Hello', ru: 'Привет' } }
13
+ before { I18n.locale = :en }
14
+
15
+ describe '#name_translations' do
16
+ subject { klass.new name_translations: translations }
17
+ its(:name_translations) { should == translations.stringify_keys }
18
+ its(:name) { should == translations[:en] }
19
+ end
20
+
21
+ describe '#name' do
22
+ subject { klass.new name: 'Hello' }
23
+ its(:name_translations) { should == { 'en' => 'Hello' } }
24
+ its(:name) { should == 'Hello' }
25
+ end
26
+
27
+ describe '#name_before_type_cast' do
28
+ let(:object) { Object.new }
29
+ subject { klass.new name: object }
30
+ its(:name_before_type_cast) { should == object }
31
+ end
32
+
33
+ context 'fallbacks' do
34
+ subject { klass.new name_translations: { ru: 'Привет' } }
35
+ context do
36
+ its(:name) { should be_nil }
37
+ end
38
+
39
+ context do
40
+ before do
41
+ require "i18n/backend/fallbacks"
42
+ I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
43
+ I18n.fallbacks.map(en: :ru)
44
+ end
45
+ its(:name) { should == 'Привет' }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe ActiveData::Model::Associations do
5
+
6
+ class Assoc
7
+ include ActiveData::Model
8
+
9
+ attribute :name
10
+ end
11
+
12
+ let(:klass) do
13
+ Class.new do
14
+ include ActiveData::Model
15
+
16
+ attribute :name
17
+ embeds_many :assocs
18
+
19
+ accepts_nested_attributes_for :assocs
20
+ end
21
+ end
22
+
23
+ let(:instance){klass.new(:name => 'world')}
24
+
25
+ context do
26
+ before { instance.assocs_attributes = [{:name => 'foo'}, {:name => 'bar'}] }
27
+ specify { instance.assocs.count.should == 2 }
28
+ specify { instance.assocs.first.name.should == 'foo' }
29
+ specify { instance.assocs.last.name.should == 'bar' }
30
+ end
31
+
32
+ context do
33
+ before { instance.assocs_attributes = {1 => {:name => 'baz'}, 2 => {:name => 'foo'}} }
34
+ specify { instance.assocs.count.should == 2 }
35
+ specify { instance.assocs.first.name.should == 'baz' }
36
+ specify { instance.assocs.last.name.should == 'foo' }
37
+ end
38
+
39
+ context do
40
+ before { instance.assocs_attributes = {1 => {:name => 'baz'}, 2 => {:name => 'foo'}} }
41
+ specify { instance.to_params.should == {
42
+ "name" => "world",
43
+ "assocs_attributes" => {'0' => {"name" => "baz"}, '1' => {"name" => "foo"}}
44
+ } }
45
+ end
46
+ end
@@ -1,7 +1,7 @@
1
1
  # encoding: UTF-8
2
2
  require 'spec_helper'
3
3
 
4
- describe ActiveData::Model::Serializable do
4
+ describe 'typecasting' do
5
5
 
6
6
  let(:klass) do
7
7
  Class.new do
@@ -10,6 +10,8 @@ describe ActiveData::Model::Serializable do
10
10
 
11
11
  attribute :string, type: String
12
12
  attribute :integer, type: Integer
13
+ attribute :float, type: Float
14
+ attribute :big_decimal, type: BigDecimal
13
15
  attribute :boolean, type: Boolean
14
16
  attribute :array, type: Array
15
17
 
@@ -32,11 +34,37 @@ describe ActiveData::Model::Serializable do
32
34
  specify{subject.tap{|s| s.integer = 'hello'}.integer.should == nil}
33
35
  specify{subject.tap{|s| s.integer = '123hello'}.integer.should == nil}
34
36
  specify{subject.tap{|s| s.integer = '123'}.integer.should == 123}
37
+ specify{subject.tap{|s| s.integer = '123.5'}.integer.should == 123}
35
38
  specify{subject.tap{|s| s.integer = 123}.integer.should == 123}
39
+ specify{subject.tap{|s| s.integer = 123.5}.integer.should == 123}
36
40
  specify{subject.tap{|s| s.integer = nil}.integer.should == nil}
37
41
  specify{subject.tap{|s| s.integer = [123]}.integer.should == nil}
38
42
  end
39
43
 
44
+ context 'float' do
45
+ specify{subject.tap{|s| s.float = 'hello'}.float.should == nil}
46
+ specify{subject.tap{|s| s.float = '123hello'}.float.should == nil}
47
+ specify{subject.tap{|s| s.float = '123'}.float.should == 123.0}
48
+ specify{subject.tap{|s| s.float = '123.'}.float.should == 123.0}
49
+ specify{subject.tap{|s| s.float = '123.5'}.float.should == 123.5}
50
+ specify{subject.tap{|s| s.float = 123}.float.should == 123.0}
51
+ specify{subject.tap{|s| s.float = 123.5}.float.should == 123.5}
52
+ specify{subject.tap{|s| s.float = nil}.float.should == nil}
53
+ specify{subject.tap{|s| s.float = [123.5]}.float.should == nil}
54
+ end
55
+
56
+ context 'big_decimal' do
57
+ specify{subject.tap{|s| s.big_decimal = 'hello'}.big_decimal.should == nil}
58
+ specify{subject.tap{|s| s.big_decimal = '123hello'}.big_decimal.should == nil}
59
+ specify{subject.tap{|s| s.big_decimal = '123'}.big_decimal.should == BigDecimal.new('123.0')}
60
+ specify{subject.tap{|s| s.big_decimal = '123.'}.big_decimal.should == BigDecimal.new('123.0')}
61
+ specify{subject.tap{|s| s.big_decimal = '123.5'}.big_decimal.should == BigDecimal.new('123.5')}
62
+ specify{subject.tap{|s| s.big_decimal = 123}.big_decimal.should == BigDecimal.new('123.0')}
63
+ specify{subject.tap{|s| s.big_decimal = 123.5}.big_decimal.should == BigDecimal.new('123.5')}
64
+ specify{subject.tap{|s| s.big_decimal = nil}.big_decimal.should == nil}
65
+ specify{subject.tap{|s| s.big_decimal = [123.5]}.big_decimal.should == nil}
66
+ end
67
+
40
68
  context 'boolean' do
41
69
  specify{subject.tap{|s| s.boolean = 'hello'}.boolean.should == nil}
42
70
  specify{subject.tap{|s| s.boolean = 'true'}.boolean.should == true}
@@ -14,10 +14,10 @@ describe ActiveData::Model do
14
14
 
15
15
  specify{model.i18n_scope.should == :active_data}
16
16
  specify{model.new.should_not be_persisted}
17
- specify{model.instantiate.should be_an_instance_of model}
18
- specify{model.instantiate.should be_persisted}
17
+ specify{model.instantiate({}).should be_an_instance_of model}
18
+ specify{model.instantiate({}).should be_persisted}
19
19
 
20
20
  context 'Fault tolerance' do
21
- specify{expect{model.new(:foo => 'bar')}.not_to raise_error}
21
+ specify{ expect { model.new(:foo => 'bar') }.not_to raise_error }
22
22
  end
23
23
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_data
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-21 00:00:00.000000000 Z
12
+ date: 2012-11-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -93,22 +93,34 @@ files:
93
93
  - Rakefile
94
94
  - active_data.gemspec
95
95
  - lib/active_data.rb
96
+ - lib/active_data/attributes/base.rb
97
+ - lib/active_data/attributes/localized.rb
96
98
  - lib/active_data/model.rb
99
+ - lib/active_data/model/associations.rb
100
+ - lib/active_data/model/associations/many.rb
97
101
  - lib/active_data/model/attributable.rb
98
102
  - lib/active_data/model/collectionizable.rb
99
103
  - lib/active_data/model/collectionizable/proxy.rb
100
104
  - lib/active_data/model/extensions.rb
101
105
  - lib/active_data/model/extensions/array.rb
106
+ - lib/active_data/model/extensions/big_decimal.rb
102
107
  - lib/active_data/model/extensions/boolean.rb
103
108
  - lib/active_data/model/extensions/date.rb
109
+ - lib/active_data/model/extensions/float.rb
104
110
  - lib/active_data/model/extensions/hash.rb
105
111
  - lib/active_data/model/extensions/integer.rb
112
+ - lib/active_data/model/extensions/localized.rb
106
113
  - lib/active_data/model/extensions/string.rb
107
- - lib/active_data/model/serializable.rb
114
+ - lib/active_data/model/localizable.rb
115
+ - lib/active_data/model/nested_attributes.rb
116
+ - lib/active_data/model/parameterizable.rb
108
117
  - lib/active_data/version.rb
118
+ - spec/lib/active_data/model/associations_spec.rb
109
119
  - spec/lib/active_data/model/attributable_spec.rb
120
+ - spec/lib/active_data/model/attributes/localized_spec.rb
110
121
  - spec/lib/active_data/model/collectionizable_spec.rb
111
- - spec/lib/active_data/model/serializable_spec.rb
122
+ - spec/lib/active_data/model/nested_attributes_spec.rb
123
+ - spec/lib/active_data/model/type_cast_spec.rb
112
124
  - spec/lib/active_data/model_spec.rb
113
125
  - spec/spec_helper.rb
114
126
  homepage: ''
@@ -123,12 +135,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
123
135
  - - ! '>='
124
136
  - !ruby/object:Gem::Version
125
137
  version: '0'
138
+ segments:
139
+ - 0
140
+ hash: 3544919037103939136
126
141
  required_rubygems_version: !ruby/object:Gem::Requirement
127
142
  none: false
128
143
  requirements:
129
144
  - - ! '>='
130
145
  - !ruby/object:Gem::Version
131
146
  version: '0'
147
+ segments:
148
+ - 0
149
+ hash: 3544919037103939136
132
150
  requirements: []
133
151
  rubyforge_project:
134
152
  rubygems_version: 1.8.24
@@ -136,8 +154,11 @@ signing_key:
136
154
  specification_version: 3
137
155
  summary: Working with hashes in AR style
138
156
  test_files:
157
+ - spec/lib/active_data/model/associations_spec.rb
139
158
  - spec/lib/active_data/model/attributable_spec.rb
159
+ - spec/lib/active_data/model/attributes/localized_spec.rb
140
160
  - spec/lib/active_data/model/collectionizable_spec.rb
141
- - spec/lib/active_data/model/serializable_spec.rb
161
+ - spec/lib/active_data/model/nested_attributes_spec.rb
162
+ - spec/lib/active_data/model/type_cast_spec.rb
142
163
  - spec/lib/active_data/model_spec.rb
143
164
  - spec/spec_helper.rb
@@ -1,18 +0,0 @@
1
- module ActiveData
2
- module Model
3
- module Serializable
4
-
5
- class UnknownAttribute < ::StandardError
6
- end
7
-
8
- def serialize value, type
9
- type.modelize(value)
10
- end
11
-
12
- def deserialize value
13
- value.respond_to? :demodelize ? value.demodelize : value.to_s
14
- end
15
-
16
- end
17
- end
18
- end