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.
- data/lib/active_data/attributes/base.rb +64 -0
- data/lib/active_data/attributes/localized.rb +42 -0
- data/lib/active_data/model/associations/many.rb +24 -0
- data/lib/active_data/model/associations.rb +49 -0
- data/lib/active_data/model/attributable.rb +42 -43
- data/lib/active_data/model/collectionizable/proxy.rb +1 -1
- data/lib/active_data/model/collectionizable.rb +1 -2
- data/lib/active_data/model/extensions/array.rb +1 -5
- data/lib/active_data/model/extensions/big_decimal.rb +17 -0
- data/lib/active_data/model/extensions/boolean.rb +20 -12
- data/lib/active_data/model/extensions/date.rb +1 -1
- data/lib/active_data/model/extensions/float.rb +17 -0
- data/lib/active_data/model/extensions/hash.rb +1 -5
- data/lib/active_data/model/extensions/integer.rb +2 -2
- data/lib/active_data/model/extensions/localized.rb +22 -0
- data/lib/active_data/model/extensions/string.rb +1 -1
- data/lib/active_data/model/extensions.rb +4 -0
- data/lib/active_data/model/localizable.rb +31 -0
- data/lib/active_data/model/nested_attributes.rb +52 -0
- data/lib/active_data/model/parameterizable.rb +29 -0
- data/lib/active_data/model.rb +16 -4
- data/lib/active_data/version.rb +1 -1
- data/spec/lib/active_data/model/associations_spec.rb +35 -0
- data/spec/lib/active_data/model/attributable_spec.rb +15 -13
- data/spec/lib/active_data/model/attributes/localized_spec.rb +48 -0
- data/spec/lib/active_data/model/nested_attributes_spec.rb +46 -0
- data/spec/lib/active_data/model/{serializable_spec.rb → type_cast_spec.rb} +29 -1
- data/spec/lib/active_data/model_spec.rb +3 -3
- metadata +26 -5
- 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
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
46
|
-
|
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
|
57
|
-
|
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
|
-
|
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
|
-
|
64
|
+
@attributes[name]
|
76
65
|
end
|
77
66
|
|
78
67
|
def write_attribute name, value
|
79
|
-
|
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
|
@@ -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
|
@@ -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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
22
|
-
MAPPING[value
|
29
|
+
def active_data_type_cast value
|
30
|
+
MAPPING[value]
|
23
31
|
end
|
24
32
|
end
|
25
33
|
end
|
@@ -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
|
@@ -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
|
@@ -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
|
data/lib/active_data/model.rb
CHANGED
@@ -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/
|
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
|
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
|
data/lib/active_data/version.rb
CHANGED
@@ -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:
|
13
|
-
attribute(:calc, type:
|
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
|
-
|
25
|
-
its(:
|
26
|
-
its(:
|
27
|
-
its(:
|
28
|
-
its(:
|
29
|
-
its(:
|
30
|
-
|
31
|
-
specify{expect{subject.
|
32
|
-
specify{expect{subject.
|
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
|
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
|
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-
|
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/
|
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/
|
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/
|
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
|