fathom 0.3.7 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|