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.
- 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
|