fathom 0.3.7 → 0.5.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/.autotest +7 -5
- data/.document +2 -2
- data/Gemfile +9 -10
- data/{LICENSE → LICENSE.txt} +1 -1
- data/README.md +29 -90
- data/Rakefile +34 -32
- data/VERSION +1 -1
- data/fathom.gemspec +105 -0
- data/features/fathom.feature +26 -0
- data/features/step_definitions/fathom_steps.rb +23 -0
- data/features/support/env.rb +13 -0
- data/lib/ext/array.rb +6 -2
- data/lib/ext/string.rb +86 -7
- data/lib/fathom.rb +51 -88
- data/lib/fathom/behaviors/attribute_system.rb +91 -0
- data/lib/fathom/behaviors/context_behavior.rb +28 -0
- data/lib/fathom/behaviors/plugins.rb +16 -0
- data/lib/fathom/contexts/network_population.rb +47 -0
- data/lib/fathom/contexts/network_traversal.rb +4 -0
- data/lib/fathom/data/adjacency_matrix.rb +27 -0
- data/lib/fathom/data/definition.rb +22 -0
- data/lib/fathom/data/edge.rb +58 -0
- data/lib/fathom/data/network.rb +35 -0
- data/lib/fathom/data/outcome.rb +30 -0
- data/lib/fathom/data/property.rb +31 -0
- data/lib/fathom/data/variable.rb +59 -0
- data/lib/fathom/roles/general_graph_tools.rb +87 -0
- data/lib/fathom/roles/network_builder.rb +61 -0
- data/spec/fathom/behaviors/attribute_system_spec.rb +141 -0
- data/spec/fathom/behaviors/context_behavior_spec.rb +15 -0
- data/spec/fathom/behaviors/plugins_spec.rb +80 -0
- data/spec/fathom/contexts/network_population_spec.rb +55 -0
- data/spec/fathom/contexts/network_traversal_spec.rb +11 -0
- data/spec/fathom/data/adjacency_matrix_spec.rb +42 -0
- data/spec/fathom/data/definition_spec.rb +19 -0
- data/spec/fathom/data/edge_spec.rb +77 -0
- data/spec/fathom/data/network_spec.rb +72 -0
- data/spec/fathom/data/outcome_spec.rb +17 -0
- data/spec/fathom/data/property_spec.rb +17 -0
- data/spec/fathom/data/variable_spec.rb +101 -0
- data/spec/fathom/ext/array_spec.rb +17 -0
- data/spec/fathom/ext/string_spec.rb +90 -0
- data/spec/fathom/roles/general_graph_tools_spec.rb +95 -0
- data/spec/fathom/roles/network_builder_spec.rb +90 -0
- data/spec/fathom_spec.rb +28 -49
- data/spec/spec_helper.rb +7 -11
- data/spec/support/context_behavior.rb +14 -0
- data/spec/support/custom_matchers.rb +12 -0
- data/spec/support/files.rb +8 -0
- data/spec/support/network.yml +42 -0
- metadata +133 -174
- data/.bundle/config +0 -2
- data/.gitignore +0 -6
- data/Gemfile.lock +0 -42
- data/TODO.md +0 -127
- data/autotest/discover.rb +0 -1
- data/lib/ext/faster_csv.rb +0 -1
- data/lib/ext/open_struct.rb +0 -17
- data/lib/fathom/agent.rb +0 -48
- data/lib/fathom/agent/agent_cluster.rb +0 -23
- data/lib/fathom/agent/properties.rb +0 -48
- data/lib/fathom/archive/causal_graph.rb +0 -12
- data/lib/fathom/archive/concept.rb +0 -83
- data/lib/fathom/archive/conditional_probability_matrix.rb +0 -119
- data/lib/fathom/archive/inverter.rb +0 -20
- data/lib/fathom/archive/n2.rb +0 -198
- data/lib/fathom/archive/n3.rb +0 -119
- data/lib/fathom/archive/node.rb +0 -97
- data/lib/fathom/archive/noodle.rb +0 -136
- data/lib/fathom/archive/scratch.rb +0 -45
- data/lib/fathom/distributions.rb +0 -8
- data/lib/fathom/distributions/discrete_gaussian.rb +0 -44
- data/lib/fathom/distributions/discrete_uniform.rb +0 -25
- data/lib/fathom/distributions/gaussian.rb +0 -46
- data/lib/fathom/distributions/uniform.rb +0 -35
- data/lib/fathom/import.rb +0 -85
- data/lib/fathom/import/csv_import.rb +0 -59
- data/lib/fathom/import/import_node.rb +0 -17
- data/lib/fathom/import/yaml_import.rb +0 -74
- data/lib/fathom/knowledge_base.rb +0 -46
- data/lib/fathom/knowledge_base/search.rb +0 -19
- data/lib/fathom/monte_carlo_set.rb +0 -152
- data/lib/fathom/node.rb +0 -139
- data/lib/fathom/node/belief_node.rb +0 -121
- data/lib/fathom/node/cpm_node.rb +0 -100
- data/lib/fathom/node/data_collection.rb +0 -97
- data/lib/fathom/node/data_node.rb +0 -22
- data/lib/fathom/node/decision.rb +0 -11
- data/lib/fathom/node/discrete_node.rb +0 -41
- data/lib/fathom/node/fact.rb +0 -24
- data/lib/fathom/node/mc_node.rb +0 -70
- data/lib/fathom/node/node_extensions/enforced_name.rb +0 -12
- data/lib/fathom/node/node_extensions/numeric_methods.rb +0 -68
- data/lib/fathom/node/plausible_range.rb +0 -98
- data/lib/fathom/simulation.rb +0 -59
- data/lib/fathom/simulation/tick_methods.rb +0 -25
- data/lib/fathom/simulation/tick_simulation.rb +0 -12
- data/lib/fathom/value_description.rb +0 -79
- data/lib/options_hash.rb +0 -186
- data/spec/ext/array_spec.rb +0 -10
- data/spec/ext/faster_csv_spec.rb +0 -10
- data/spec/ext/open_struct_spec.rb +0 -20
- data/spec/ext/string_spec.rb +0 -7
- data/spec/fathom/agent/agent_cluster_spec.rb +0 -17
- data/spec/fathom/agent_spec.rb +0 -51
- data/spec/fathom/distributions/discrete_gaussian_spec.rb +0 -64
- data/spec/fathom/distributions/discrete_uniform_spec.rb +0 -0
- data/spec/fathom/distributions/gaussian_spec.rb +0 -64
- data/spec/fathom/distributions/uniform_spec.rb +0 -0
- data/spec/fathom/import/csv_import_spec.rb +0 -52
- data/spec/fathom/import/import_node_spec.rb +0 -10
- data/spec/fathom/import/yaml_import_spec.rb +0 -73
- data/spec/fathom/import_spec.rb +0 -36
- data/spec/fathom/knowledge_base_spec.rb +0 -20
- data/spec/fathom/monte_carlo_set_spec.rb +0 -149
- data/spec/fathom/node/belief_node_spec.rb +0 -180
- data/spec/fathom/node/cpm_node_spec.rb +0 -144
- data/spec/fathom/node/data_collection_spec.rb +0 -26
- data/spec/fathom/node/data_node_spec.rb +0 -102
- data/spec/fathom/node/decision_spec.rb +0 -15
- data/spec/fathom/node/discrete_node_spec.rb +0 -56
- data/spec/fathom/node/fact_spec.rb +0 -33
- data/spec/fathom/node/mc_node_spec.rb +0 -66
- data/spec/fathom/node/node_extensions/enforced_name_spec.rb +0 -15
- data/spec/fathom/node/node_extensions/numeric_methods_spec.rb +0 -124
- data/spec/fathom/node/plausible_range_spec.rb +0 -151
- data/spec/fathom/node_spec.rb +0 -172
- data/spec/fathom/simulation/tick_simulation_spec.rb +0 -32
- data/spec/fathom/simulation_spec.rb +0 -24
- data/spec/fathom/value_description_spec.rb +0 -70
- data/spec/support/demo.yml +0 -17
- data/spec/support/demo_agent.rb +0 -8
- data/spec/support/dummy_numeric_node.rb +0 -8
- data/spec/support/fact.yml +0 -11
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Fathom
|
|
2
|
+
class AdjacencyMatrix
|
|
3
|
+
|
|
4
|
+
include Enumerable
|
|
5
|
+
|
|
6
|
+
def initialize(default=0)
|
|
7
|
+
@default = default
|
|
8
|
+
@store = Hash.new(default)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def [](parent, child)
|
|
12
|
+
@store[[parent, child]]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def []=(parent, child, value)
|
|
16
|
+
if value == @default
|
|
17
|
+
@store.delete([parent, child]) # preserves a little memory
|
|
18
|
+
else
|
|
19
|
+
@store[[parent, child]] = value
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def each
|
|
24
|
+
@store.each {|k, v| yield(k,v)}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Fathom
|
|
2
|
+
class Definition
|
|
3
|
+
|
|
4
|
+
# ============
|
|
5
|
+
# = Behavior =
|
|
6
|
+
# ============
|
|
7
|
+
extend Plugins
|
|
8
|
+
plugin AttributeSystem
|
|
9
|
+
|
|
10
|
+
# ==============
|
|
11
|
+
# = Attributes =
|
|
12
|
+
# ==============
|
|
13
|
+
attribute :for
|
|
14
|
+
attribute :given
|
|
15
|
+
attribute :table
|
|
16
|
+
|
|
17
|
+
def initialize(attrs={})
|
|
18
|
+
@attributes = attrs
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end # Definition
|
|
22
|
+
end # Fathom
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Fathom
|
|
2
|
+
class Edge
|
|
3
|
+
|
|
4
|
+
# =============
|
|
5
|
+
# Class Methods
|
|
6
|
+
# =============
|
|
7
|
+
class << self
|
|
8
|
+
def infer(obj, optional_child=nil)
|
|
9
|
+
return new(:parent => Variable.infer(obj), :child => Variable.infer(optional_child)) if optional_child
|
|
10
|
+
|
|
11
|
+
case obj
|
|
12
|
+
when Edge
|
|
13
|
+
obj
|
|
14
|
+
when Array
|
|
15
|
+
new(:parent => Variable.infer(obj[0]), :child => Variable.infer(obj[1]))
|
|
16
|
+
when Hash
|
|
17
|
+
new(obj)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# ============
|
|
23
|
+
# = Behavior =
|
|
24
|
+
# ============
|
|
25
|
+
extend Plugins
|
|
26
|
+
plugin AttributeSystem
|
|
27
|
+
|
|
28
|
+
# ==============
|
|
29
|
+
# = Attributes =
|
|
30
|
+
# ==============
|
|
31
|
+
attribute :is_directed, true
|
|
32
|
+
attribute :parent
|
|
33
|
+
attribute :child
|
|
34
|
+
|
|
35
|
+
def initialize(attributes={})
|
|
36
|
+
attributes[:parent] = Variable.infer(attributes[:parent])
|
|
37
|
+
attributes[:child] = Variable.infer(attributes[:child])
|
|
38
|
+
@attributes = attributes
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# ============================================================
|
|
42
|
+
# = Override setters on parent and child to ensure Variables =
|
|
43
|
+
# ============================================================
|
|
44
|
+
def parent=(obj)
|
|
45
|
+
send(self.class.attributes_proxy)[:parent] = Variable.infer(obj)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def child=(obj)
|
|
49
|
+
send(self.class.attributes_proxy)[:child] = Variable.infer(obj)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def eql(other)
|
|
53
|
+
return false unless other.is_a?(Edge)
|
|
54
|
+
self.parent == other.parent and self.child == other.child
|
|
55
|
+
end
|
|
56
|
+
alias :== :eql
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Fathom
|
|
2
|
+
class Network
|
|
3
|
+
|
|
4
|
+
# ============
|
|
5
|
+
# = Behavior =
|
|
6
|
+
# ============
|
|
7
|
+
extend Plugins
|
|
8
|
+
include Enumerable
|
|
9
|
+
plugin AttributeSystem
|
|
10
|
+
|
|
11
|
+
# ==============
|
|
12
|
+
# = Attributes =
|
|
13
|
+
# ==============
|
|
14
|
+
attribute :name
|
|
15
|
+
attribute :variables, []
|
|
16
|
+
attribute :properties, []
|
|
17
|
+
attribute :edges, []
|
|
18
|
+
attribute :definitions, []
|
|
19
|
+
|
|
20
|
+
def initialize(attrs={})
|
|
21
|
+
@attributes = attrs
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# ====================
|
|
25
|
+
# = Instance Methods =
|
|
26
|
+
# ====================
|
|
27
|
+
def each(&block)
|
|
28
|
+
variables.each do |variable|
|
|
29
|
+
yield variable
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
alias :each_variable :each
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Fathom
|
|
2
|
+
class Outcome
|
|
3
|
+
|
|
4
|
+
# =================
|
|
5
|
+
# = Class Methods =
|
|
6
|
+
# =================
|
|
7
|
+
class << self
|
|
8
|
+
def infer(obj)
|
|
9
|
+
new(:value => obj)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# ============
|
|
14
|
+
# = Behavior =
|
|
15
|
+
# ============
|
|
16
|
+
extend Plugins
|
|
17
|
+
plugin AttributeSystem
|
|
18
|
+
|
|
19
|
+
# ==============
|
|
20
|
+
# = Attributes =
|
|
21
|
+
# ==============
|
|
22
|
+
attribute :value
|
|
23
|
+
|
|
24
|
+
def initialize(attrs={})
|
|
25
|
+
@attributes = attrs
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end # Outcome
|
|
29
|
+
end # Fathom
|
|
30
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Fathom
|
|
2
|
+
class Property
|
|
3
|
+
|
|
4
|
+
# =================
|
|
5
|
+
# = Class Methods =
|
|
6
|
+
# =================
|
|
7
|
+
class << self
|
|
8
|
+
def infer(obj)
|
|
9
|
+
new(:value => obj)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# ============
|
|
14
|
+
# = Behavior =
|
|
15
|
+
# ============
|
|
16
|
+
extend Plugins
|
|
17
|
+
plugin AttributeSystem
|
|
18
|
+
|
|
19
|
+
# ==============
|
|
20
|
+
# = Attributes =
|
|
21
|
+
# ==============
|
|
22
|
+
attribute :value
|
|
23
|
+
|
|
24
|
+
def initialize(attrs={})
|
|
25
|
+
@attributes = attrs
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end # Property
|
|
29
|
+
end # Fathom
|
|
30
|
+
|
|
31
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Fathom
|
|
2
|
+
class Variable
|
|
3
|
+
|
|
4
|
+
# =================
|
|
5
|
+
# = Class Methods =
|
|
6
|
+
# =================
|
|
7
|
+
class << self
|
|
8
|
+
def infer(obj)
|
|
9
|
+
case obj
|
|
10
|
+
when Variable
|
|
11
|
+
obj
|
|
12
|
+
when Hash
|
|
13
|
+
new(obj)
|
|
14
|
+
when Symbol, String
|
|
15
|
+
new(:name => obj)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# ============
|
|
21
|
+
# = Behavior =
|
|
22
|
+
# ============
|
|
23
|
+
extend Plugins
|
|
24
|
+
plugin AttributeSystem
|
|
25
|
+
|
|
26
|
+
# ==============
|
|
27
|
+
# = Attributes =
|
|
28
|
+
# ==============
|
|
29
|
+
attribute :id
|
|
30
|
+
attribute :name
|
|
31
|
+
attribute :properties
|
|
32
|
+
attribute :outcomes, [true, false]
|
|
33
|
+
attribute :prior_odds
|
|
34
|
+
|
|
35
|
+
def initialize(attrs={})
|
|
36
|
+
attrs[:id] ||= UUID.generate
|
|
37
|
+
@attributes = attrs
|
|
38
|
+
self.prior_odds ||= uniform_set.dup
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def underscored_name
|
|
42
|
+
name.to_s.gsub(/\W/, "_").underscore
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def eql(other)
|
|
46
|
+
return false unless other.is_a?(Variable)
|
|
47
|
+
self.name == other.name
|
|
48
|
+
end
|
|
49
|
+
alias :== :eql
|
|
50
|
+
|
|
51
|
+
protected
|
|
52
|
+
def uniform_set
|
|
53
|
+
size = self.outcomes.size
|
|
54
|
+
Array.new(size, 1/size.to_f)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end # class Data
|
|
58
|
+
|
|
59
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module Fathom
|
|
2
|
+
module GeneralGraphTools
|
|
3
|
+
|
|
4
|
+
def adjacency_matrix
|
|
5
|
+
@adjacency_matrix ||= AdjacencyMatrix.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def add_edge(parent, child)
|
|
9
|
+
self.variables |= [parent, child]
|
|
10
|
+
self.adjacency_matrix[parent, child] = 1
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def remove_edge(parent, child)
|
|
14
|
+
return false unless self.variables.include?(parent) and self.variables.include?(child)
|
|
15
|
+
self.adjacency_matrix[parent, child] = 0
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def at(parent, child)
|
|
19
|
+
self.adjacency_matrix[parent, child]
|
|
20
|
+
end
|
|
21
|
+
alias :[] :at
|
|
22
|
+
|
|
23
|
+
def edge?(parent, child)
|
|
24
|
+
return false unless ([parent, child] - self.variables).empty?
|
|
25
|
+
self.adjacency_matrix[parent, child] == 1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Implemented as an out-degree
|
|
29
|
+
def degree(node)
|
|
30
|
+
found = self.adjacency_matrix.select do |(parent, child), value|
|
|
31
|
+
parent == node
|
|
32
|
+
end
|
|
33
|
+
found.size
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def each_vertex
|
|
37
|
+
self.variables.each {|v| yield v}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def each_edge
|
|
41
|
+
self.adjacency_matrix.each {|(parent, child), value| yield parent, child}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Find whether the graph is connected, based on notes here:
|
|
45
|
+
# http://www.ics.uci.edu/~eppstein/161/960201.html
|
|
46
|
+
# This assumes that all variables in the network have been added
|
|
47
|
+
# to the adjacency matrix.
|
|
48
|
+
def connected?
|
|
49
|
+
x = self.variables[0] # Arbitrary first node
|
|
50
|
+
reachable_vertices = [x]
|
|
51
|
+
vertices_to_explore = [x]
|
|
52
|
+
|
|
53
|
+
while !vertices_to_explore.empty?
|
|
54
|
+
y = vertices_to_explore.shift
|
|
55
|
+
self.each_edge do |parent, child|
|
|
56
|
+
unless reachable_vertices.include?(child)
|
|
57
|
+
vertices_to_explore << child
|
|
58
|
+
reachable_vertices << child
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
reachable_vertices.size < self.variables.size ? false : true
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def euler_circuit?
|
|
68
|
+
return false if !connected?
|
|
69
|
+
self.each_vertex do |v|
|
|
70
|
+
return false if degree(v) % 2 != 0
|
|
71
|
+
end
|
|
72
|
+
true
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def euler_path?
|
|
76
|
+
return false if !connected?
|
|
77
|
+
odd=0
|
|
78
|
+
self.each_vertex do |v|
|
|
79
|
+
if degree(v) % 2 == 1
|
|
80
|
+
odd += 1
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
odd <= 2
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Fathom
|
|
2
|
+
|
|
3
|
+
# This is the role necessary for importing nodes, shaping graphs, adding edges, that sort of thing.
|
|
4
|
+
module NetworkBuilder
|
|
5
|
+
def from_hash(hash)
|
|
6
|
+
hash = serialize_hash(hash)
|
|
7
|
+
attributes.merge!(hash)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
protected
|
|
11
|
+
|
|
12
|
+
def serialize_hash(hash)
|
|
13
|
+
hash.inject({}) do |h, (key, value)|
|
|
14
|
+
|
|
15
|
+
value = [value] if attribute_should_be_an_array(key) and not value.is_a?(Array)
|
|
16
|
+
|
|
17
|
+
if klass = serialization_class_for(key)
|
|
18
|
+
value = infer_serialized_array(value, klass)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
h[key] = value
|
|
22
|
+
|
|
23
|
+
h
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def serialization_class_for(attribute)
|
|
28
|
+
serialization_map[attribute]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def serialization_map
|
|
32
|
+
@serialization_map ||= {
|
|
33
|
+
definitions: Definition,
|
|
34
|
+
edges: Edge,
|
|
35
|
+
properties: Property,
|
|
36
|
+
variables: Variable
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def attribute_should_be_an_array(attribute)
|
|
41
|
+
array_conversion_attributes.include?(attribute)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def array_conversion_attributes
|
|
45
|
+
@array_conversion_map ||= [:properties, :variables, :edges, :definitions]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Uses the Class.infer, if available. Otherwise initializes the value passed in.
|
|
49
|
+
def infer_serialized_array(array, klass)
|
|
50
|
+
array.map do |e|
|
|
51
|
+
if klass == e.class
|
|
52
|
+
e
|
|
53
|
+
elsif klass.respond_to?(:infer)
|
|
54
|
+
klass.infer(e)
|
|
55
|
+
else
|
|
56
|
+
klass.new(e)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
|
2
|
+
|
|
3
|
+
include Fathom
|
|
4
|
+
|
|
5
|
+
describe AttributeSystem do
|
|
6
|
+
|
|
7
|
+
context "when checking the default nature of a class that has plugged in AttributeSystem" do
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
@class = Class.new do
|
|
11
|
+
extend Plugins
|
|
12
|
+
plugin AttributeSystem
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should have an attributes getter" do
|
|
17
|
+
obj = @class.new
|
|
18
|
+
obj.should respond_to(:attributes)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should default the attributes to a Hash" do
|
|
22
|
+
obj = @class.new
|
|
23
|
+
obj.attributes.should eql({})
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "when proxying to another object" do
|
|
28
|
+
|
|
29
|
+
before do
|
|
30
|
+
@class = Class.new do
|
|
31
|
+
extend Plugins
|
|
32
|
+
plugin AttributeSystem
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should default the attributes_proxy to :attributes" do
|
|
37
|
+
@class.attributes_proxy.should eql(:attributes)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should be able to set_attributes_proxy" do
|
|
41
|
+
@class.set_attributes_proxy :some_method
|
|
42
|
+
@class.attributes_proxy.should eql(:some_method)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context "when defining an attribute" do
|
|
47
|
+
|
|
48
|
+
before do
|
|
49
|
+
@class = Class.new do
|
|
50
|
+
extend Plugins
|
|
51
|
+
plugin AttributeSystem
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "should have an attribute method" do
|
|
56
|
+
@class.should respond_to(:attribute)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "should be able to take an attribute name and have a setter and getter on that" do
|
|
60
|
+
@class.attribute(:name)
|
|
61
|
+
obj = @class.new
|
|
62
|
+
obj.name = :name
|
|
63
|
+
obj.name.should eql(:name)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should be able to read from the attributes object" do
|
|
67
|
+
@class = Class.new do
|
|
68
|
+
extend Plugins
|
|
69
|
+
plugin AttributeSystem
|
|
70
|
+
attribute :name
|
|
71
|
+
def initialize
|
|
72
|
+
@attributes = {:name => :value}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
obj = @class.new
|
|
76
|
+
obj.name.should eql(:value)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "should follow the proxied attributes, if it's been changed" do
|
|
80
|
+
@class = Class.new do
|
|
81
|
+
extend Plugins
|
|
82
|
+
plugin AttributeSystem
|
|
83
|
+
|
|
84
|
+
attribute :name
|
|
85
|
+
set_attributes_proxy :alternate_attributes
|
|
86
|
+
|
|
87
|
+
def alternate_attributes
|
|
88
|
+
{:name => :value}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
obj = @class.new
|
|
92
|
+
obj.name.should eql(:value)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should be able to set an attribute with a default" do
|
|
96
|
+
@class.attribute(:name, :default)
|
|
97
|
+
obj = @class.new
|
|
98
|
+
obj.name.should eql(:default)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "should not use the default if the attribute has been set" do
|
|
102
|
+
@class.attribute(:name, :default)
|
|
103
|
+
obj = @class.new
|
|
104
|
+
obj.name.should eql(:default)
|
|
105
|
+
obj.name = :changed
|
|
106
|
+
obj.name.should eql(:changed)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "should not use the default if the underlying attribute has been set" do
|
|
110
|
+
@class = Class.new do
|
|
111
|
+
extend Plugins
|
|
112
|
+
plugin AttributeSystem
|
|
113
|
+
attribute :name, :default
|
|
114
|
+
|
|
115
|
+
def initialize(attrs={})
|
|
116
|
+
@attributes = attrs
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
obj = @class.new :name => :changed
|
|
121
|
+
obj.name.should eql(:changed)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
it "should not use the default if the underlying attribute has been set" do
|
|
126
|
+
@class = Class.new do
|
|
127
|
+
extend Plugins
|
|
128
|
+
plugin AttributeSystem
|
|
129
|
+
attribute :name, :default
|
|
130
|
+
|
|
131
|
+
def initialize(attrs={})
|
|
132
|
+
@attributes = attrs
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
obj = @class.new :name => nil
|
|
137
|
+
obj.name.should be_nil
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
end
|