active_data 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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