architect4r 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +53 -0
  4. data/Guardfile +10 -0
  5. data/License +20 -0
  6. data/README.md +62 -0
  7. data/Rakefile +40 -0
  8. data/ReleaseNotes.md +33 -0
  9. data/Roadmap.md +31 -0
  10. data/Specs.md +21 -0
  11. data/architect4r.gemspec +31 -0
  12. data/lib/architect4r.rb +66 -0
  13. data/lib/architect4r/adapters/carrier_wave.rb +64 -0
  14. data/lib/architect4r/core/configuration.rb +148 -0
  15. data/lib/architect4r/core/cypher_methods.rb +47 -0
  16. data/lib/architect4r/core/management_methods.rb +129 -0
  17. data/lib/architect4r/core/node_methods.rb +73 -0
  18. data/lib/architect4r/core/relationship_methods.rb +82 -0
  19. data/lib/architect4r/generic_node.rb +7 -0
  20. data/lib/architect4r/has_node.rb +80 -0
  21. data/lib/architect4r/instance_manager.rb +47 -0
  22. data/lib/architect4r/model/callbacks.rb +19 -0
  23. data/lib/architect4r/model/connection.rb +29 -0
  24. data/lib/architect4r/model/links_query_interface.rb +23 -0
  25. data/lib/architect4r/model/node.rb +117 -0
  26. data/lib/architect4r/model/persistency.rb +95 -0
  27. data/lib/architect4r/model/properties.rb +166 -0
  28. data/lib/architect4r/model/queries.rb +38 -0
  29. data/lib/architect4r/model/relationship.rb +105 -0
  30. data/lib/architect4r/model/relationships.rb +16 -0
  31. data/lib/architect4r/model/validations.rb +11 -0
  32. data/lib/architect4r/server.rb +96 -0
  33. data/lib/architect4r/version.rb +3 -0
  34. data/spec/architect4r_spec.rb +9 -0
  35. data/spec/core/configuration_spec.rb +54 -0
  36. data/spec/core/cypher_methods_spec.rb +29 -0
  37. data/spec/core/node_methods_spec.rb +47 -0
  38. data/spec/core/relationship_methods_spec.rb +92 -0
  39. data/spec/fixtures/architect4r.yml +21 -0
  40. data/spec/fixtures/graph.db.default/active_tx_log +1 -0
  41. data/spec/fixtures/graph.db.default/index/lucene-store.db +0 -0
  42. data/spec/fixtures/graph.db.default/index/lucene.log.1 +0 -0
  43. data/spec/fixtures/graph.db.default/index/lucene.log.active +0 -0
  44. data/spec/fixtures/graph.db.default/index/lucene.log.v0 +0 -0
  45. data/spec/fixtures/graph.db.default/index/lucene.log.v1 +0 -0
  46. data/spec/fixtures/graph.db.default/index/lucene.log.v2 +0 -0
  47. data/spec/fixtures/graph.db.default/lock +0 -0
  48. data/spec/fixtures/graph.db.default/messages.log +183 -0
  49. data/spec/fixtures/graph.db.default/neostore +0 -0
  50. data/spec/fixtures/graph.db.default/neostore.id +0 -0
  51. data/spec/fixtures/graph.db.default/neostore.nodestore.db +0 -0
  52. data/spec/fixtures/graph.db.default/neostore.nodestore.db.id +0 -0
  53. data/spec/fixtures/graph.db.default/neostore.propertystore.db +0 -0
  54. data/spec/fixtures/graph.db.default/neostore.propertystore.db.arrays +0 -0
  55. data/spec/fixtures/graph.db.default/neostore.propertystore.db.arrays.id +0 -0
  56. data/spec/fixtures/graph.db.default/neostore.propertystore.db.id +0 -0
  57. data/spec/fixtures/graph.db.default/neostore.propertystore.db.index +0 -0
  58. data/spec/fixtures/graph.db.default/neostore.propertystore.db.index.id +0 -0
  59. data/spec/fixtures/graph.db.default/neostore.propertystore.db.index.keys +0 -0
  60. data/spec/fixtures/graph.db.default/neostore.propertystore.db.index.keys.id +0 -0
  61. data/spec/fixtures/graph.db.default/neostore.propertystore.db.strings +0 -0
  62. data/spec/fixtures/graph.db.default/neostore.propertystore.db.strings.id +0 -0
  63. data/spec/fixtures/graph.db.default/neostore.relationshipstore.db +0 -0
  64. data/spec/fixtures/graph.db.default/neostore.relationshipstore.db.id +0 -0
  65. data/spec/fixtures/graph.db.default/neostore.relationshiptypestore.db +0 -0
  66. data/spec/fixtures/graph.db.default/neostore.relationshiptypestore.db.id +0 -0
  67. data/spec/fixtures/graph.db.default/neostore.relationshiptypestore.db.names +0 -0
  68. data/spec/fixtures/graph.db.default/neostore.relationshiptypestore.db.names.id +0 -0
  69. data/spec/fixtures/graph.db.default/nioneo_logical.log.1 +0 -0
  70. data/spec/fixtures/graph.db.default/nioneo_logical.log.active +0 -0
  71. data/spec/fixtures/graph.db.default/tm_tx_log.1 +0 -0
  72. data/spec/fixtures/graph.db.default/upgrade_backup/active_tx_log +1 -0
  73. data/spec/fixtures/graph.db.default/upgrade_backup/messages.log +142 -0
  74. data/spec/fixtures/graph.db.default/upgrade_backup/neostore +0 -0
  75. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.id +0 -0
  76. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.nodestore.db +0 -0
  77. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.nodestore.db.id +0 -0
  78. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db +0 -0
  79. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.arrays +0 -0
  80. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.arrays.id +0 -0
  81. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.id +0 -0
  82. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.index +0 -0
  83. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.index.id +0 -0
  84. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.index.keys +0 -0
  85. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.index.keys.id +0 -0
  86. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.strings +0 -0
  87. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.strings.id +0 -0
  88. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshipstore.db +0 -0
  89. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshipstore.db.id +0 -0
  90. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshiptypestore.db +0 -0
  91. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshiptypestore.db.id +0 -0
  92. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshiptypestore.db.names +0 -0
  93. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshiptypestore.db.names.id +0 -0
  94. data/spec/fixtures/graph.db.default/upgrade_backup/nioneo_logical.log.active +0 -0
  95. data/spec/fixtures/graph.db.default/upgrade_backup/nioneo_logical.log.v0 +0 -0
  96. data/spec/fixtures/graph.db.default/upgrade_backup/nioneo_logical.log.v1 +0 -0
  97. data/spec/fixtures/graph.db.default/upgrade_backup/nioneo_logical.log.v2 +0 -0
  98. data/spec/fixtures/graph.db.default/upgrade_backup/tm_tx_log.1 +0 -0
  99. data/spec/has_node_spec.rb +87 -0
  100. data/spec/model/links_query_interface_spec.rb +22 -0
  101. data/spec/model/links_spec.rb +26 -0
  102. data/spec/model/node_spec.rb +48 -0
  103. data/spec/model/persistency_spec.rb +98 -0
  104. data/spec/model/properties_spec.rb +165 -0
  105. data/spec/model/queries_spec.rb +50 -0
  106. data/spec/model/relationship_spec.rb +63 -0
  107. data/spec/model/validations_spec.rb +31 -0
  108. data/spec/server_spec.rb +33 -0
  109. data/spec/spec_helper.rb +115 -0
  110. metadata +377 -0
@@ -0,0 +1,7 @@
1
+ module Architect4r
2
+
3
+ class GenericNode < Model::Node
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,80 @@
1
+ module Architect4r
2
+
3
+ # has_node :node, AccountNode, :sync => [:firstname, :lastname]
4
+ module HasNode
5
+
6
+ # Easier integration pattern
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+
11
+ def has_node(attr_name, model_name, options={})
12
+
13
+ #if self.respond_to?(:after_create)
14
+ after_create :"architect4r_create_#{attr_name}"
15
+ after_update :"architect4r_sync_#{attr_name}"
16
+ after_destroy :"architect4r_destroy_#{attr_name}"
17
+ #end
18
+
19
+ # Create a neo4j node
20
+ define_method("architect4r_create_#{attr_name}") do
21
+ return nil unless self.id
22
+
23
+ new_node = model_name.new
24
+ new_node.write_attribute(:architect4r_sync_id, self.id)
25
+
26
+ options[:sync].to_a.each do |prop|
27
+ new_node.send("#{prop}=", self.send(prop))
28
+ end
29
+
30
+ if new_node.save
31
+ new_node
32
+ else
33
+ nil
34
+ end
35
+ instance_variable_set("@#{attr_name}", new_node)
36
+ end
37
+
38
+ # Create a getter method
39
+ define_method(attr_name) do
40
+ # Only available if the database record is already created
41
+ return nil unless self.id
42
+
43
+ # Get or create the graph db node
44
+ the_node = instance_variable_get("@#{attr_name}")
45
+ the_node ||= begin
46
+ linked_node = model_name.find_by_cypher("start s=node(#{model_name.model_root.id}) match s<-[:model_type]-d where d.architect4r_sync_id = #{self.id} return d", "d").first
47
+ linked_node ||= self.send("architect4r_create_#{attr_name}")
48
+ instance_variable_set("@#{attr_name}", linked_node)
49
+ end
50
+ end
51
+
52
+ define_method("architect4r_destroy_#{attr_name}") do
53
+ self.send("#{attr_name}").destroy
54
+ end
55
+
56
+
57
+ # Keep the graph node in sync, so it reflects the name
58
+ #
59
+ define_method("architect4r_sync_#{attr_name}") do
60
+ changed = false
61
+
62
+ options[:sync].to_a.each do |prop|
63
+ if self.node.send("#{prop}") != self.send(prop)
64
+ self.node.send("#{prop}=", self.send(prop))
65
+ changed = true
66
+ end
67
+ end
68
+
69
+ if changed
70
+ self.node.save
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,47 @@
1
+ module Architect4r
2
+ class InstanceManager
3
+
4
+ def initialize(path)
5
+ @path = path
6
+ end
7
+
8
+ def server_path
9
+ @path
10
+ end
11
+
12
+ def start
13
+ %x[#{server_path}/bin/neo4j start]
14
+ end
15
+
16
+ def stop
17
+ %x[#{server_path}/bin/neo4j stop]
18
+ end
19
+
20
+ def restart
21
+ %x[#{server_path}/bin/neo4j restart]
22
+ end
23
+
24
+ def reset
25
+ self.stop
26
+
27
+ # Reset the database
28
+ FileUtils.rm_rf("#{server_path}/data/graph.db")
29
+ FileUtils.mkdir("#{server_path}/data/graph.db")
30
+
31
+ # Remove log files
32
+ FileUtils.rm_rf("#{server_path}/data/log")
33
+ FileUtils.mkdir("#{server_path}/data/log")
34
+
35
+ # Start the server
36
+ self.start
37
+ end
38
+
39
+ def reset_to_sample_data(from)
40
+ self.stop
41
+ FileUtils.rm_rf("#{server_path}/data/graph.db")
42
+ FileUtils.cp_r(from, "#{server_path}/data/graph.db/")
43
+ self.start
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ module Architect4r
4
+ module Model
5
+
6
+ module Callbacks
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ extend ActiveModel::Callbacks
11
+ include ActiveModel::Validations::Callbacks
12
+
13
+ define_model_callbacks :initialize, :only => :after
14
+ define_model_callbacks :create, :destroy, :save, :update
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ module Architect4r
2
+ module Model
3
+ module Connection
4
+ extend ActiveSupport::Concern
5
+
6
+ module InstanceMethods
7
+
8
+ def connection
9
+ self.class.connection
10
+ end
11
+
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ def use_server(server)
17
+ @connection = server
18
+ end
19
+
20
+ def connection
21
+ # TODO: apply configuration
22
+ @connection || Server.new
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ module Architect4r
2
+ module Model
3
+ class LinksQueryInterface
4
+
5
+ def initialize(owner)
6
+ @owner = owner
7
+ end
8
+
9
+ def incoming
10
+
11
+ end
12
+
13
+ def outgoing
14
+
15
+ end
16
+
17
+ def both
18
+
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,117 @@
1
+ module Architect4r
2
+
3
+ module Model
4
+
5
+ class Node
6
+
7
+ #
8
+ # architect4r extensions
9
+ #
10
+ include Architect4r::Model::Connection
11
+ include Architect4r::Model::Callbacks
12
+ include Architect4r::Model::Persistency
13
+ include Architect4r::Model::Queries
14
+ include Architect4r::Model::Relationships
15
+
16
+ def self.inherited(subklass)
17
+ super
18
+ subklass.send(:include, ActiveModel::Conversion)
19
+ subklass.extend ActiveModel::Naming
20
+ subklass.send(:include, Architect4r::Model::Properties)
21
+ subklass.send(:include, Architect4r::Model::Validations)
22
+
23
+ subklass.class_exec do
24
+
25
+ def self.model_root
26
+ @model_root ||= begin
27
+ # Check if there is already a model root,
28
+ query = "start root = node(0) match (root)-[r:#{ model_root_relation_type}]->(x) where r.architect4r_type and r.architect4r_type = '#{name}' return x"
29
+ the_root = connection.execute_cypher(query).to_a.first
30
+ the_root &&= the_root['x']
31
+
32
+ # otherwise create one
33
+ the_root ||= begin
34
+ m_root = connection.create_node(:name => "#{name} Root")
35
+ connection.create_relationship(0, m_root, model_root_relation_type, { 'architect4r_type' => name })
36
+ m_root
37
+ end
38
+
39
+ # Return model root node
40
+ GenericNode.send(:build_from_database, the_root)
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+
47
+ attr_accessor :raw_data
48
+
49
+ def initialize(properties={})
50
+ run_callbacks :initialize do
51
+ parse_properties(properties)
52
+ end
53
+ end
54
+
55
+ # Create the document. Validation is enabled by default and will return
56
+ # false if the document is not valid. If all goes well, the document will
57
+ # be returned.
58
+ def create(options = {})
59
+ run_callbacks :create do
60
+ run_callbacks :save do
61
+ # only create valid records
62
+ return false unless perform_validations(options)
63
+
64
+ # perform creation
65
+ if result = connection.create_node(self._to_database_hash)
66
+ self.raw_data = result
67
+
68
+ # Link the node with a model root node
69
+ connection.create_relationship(self.id, self.class.model_root.id, 'model_type')
70
+ end
71
+
72
+ # if something goes wrong we receive a nil value and return false
73
+ !result.nil?
74
+ end
75
+ end
76
+ end
77
+
78
+ # Trigger the callbacks (before, after, around)
79
+ # only if the document isn't new
80
+ def update(options = {})
81
+ run_callbacks :update do
82
+ run_callbacks :save do
83
+ # Check if record can be updated
84
+ raise "Cannot save a destroyed document!" if destroyed?
85
+ raise "Calling #{self.class.name}#update on document that has not been created!" if new?
86
+
87
+ # Check if we can continue
88
+ return false unless perform_validations(options)
89
+
90
+ # perform update
91
+ result = connection.update_node(self.id, self._to_database_hash)
92
+
93
+ # if something goes wrong we receive a nil value and return false
94
+ !result.nil?
95
+ end
96
+ end
97
+ end
98
+
99
+ def destroy
100
+ run_callbacks :destroy do
101
+ if result = connection.delete_node(self.id)
102
+ @_destroyed = true
103
+ self.freeze
104
+ end
105
+ result
106
+ end
107
+ end
108
+
109
+ def self.model_root_relation_type
110
+ 'model_root'
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,95 @@
1
+ module Architect4r
2
+ module Model
3
+ module Persistency
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+
8
+ end
9
+
10
+ module InstanceMethods
11
+
12
+ # Creates the document in the db. Raises an exception
13
+ # if the document is not created properly.
14
+ def create!(options = {})
15
+ self.fail_validate!(self) unless self.create(options)
16
+ end
17
+
18
+ def save(options = {})
19
+ self.new? ? create(options) : update(options)
20
+ end
21
+
22
+ def save!
23
+ self.class.fail_validate!(self) unless self.save
24
+ true
25
+ end
26
+
27
+ def id
28
+ @id ||= if raw_data && raw_data['self'].present?
29
+ raw_data['self'].split('/').last.to_i
30
+ else
31
+ nil
32
+ end
33
+ end
34
+ alias :to_key :id
35
+ alias :to_param :id
36
+
37
+ def new?
38
+ # Persisted objects always have an id.
39
+ id.nil?
40
+ end
41
+ alias :new_record? :new?
42
+
43
+ def destroyed?
44
+ !!@_destroyed
45
+ end
46
+
47
+ def persisted?
48
+ !new? && !destroyed?
49
+ end
50
+
51
+ # Update the document's attributes and save. For example:
52
+ def update_attributes(hash)
53
+ update_attributes_without_saving hash
54
+ save
55
+ end
56
+
57
+ protected
58
+
59
+ def perform_validations(options = {})
60
+ (options[:validate].presence || true) ? valid? : true
61
+ end
62
+
63
+ end
64
+
65
+ module ClassMethods
66
+
67
+ def create(*args, &block)
68
+ instance = new(*args, &block)
69
+ instance.create
70
+ instance
71
+ end
72
+
73
+ # Defines an instance and save it directly to the database
74
+ def create!(*args, &block)
75
+ instance = new(*args, &block)
76
+ instance.create!
77
+ instance
78
+ end
79
+
80
+ protected
81
+
82
+ # Create an object from the database data
83
+ def build_from_database(data)
84
+ return nil if data.blank?
85
+
86
+ # Create an instance of the object
87
+ obj = self.new(data['data'])
88
+ obj.raw_data = data
89
+ obj
90
+ end
91
+
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,166 @@
1
+ module Architect4r
2
+
3
+ module Model
4
+
5
+ module Properties
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute(:properties) unless respond_to?(:properties)
10
+ self.properties ||= {}
11
+ end
12
+
13
+ module InstanceMethods
14
+ def parse_properties(properties = {})
15
+ @properties_data = {}
16
+ update_attributes_without_saving(properties)
17
+ end
18
+
19
+ # Return a hash of all properties which can be transformed into json
20
+ # Remove all nil values, as those cannot be stored in the graph
21
+ def _to_database_hash
22
+ @properties_data.merge('architect4r_type' => self.class.name).
23
+ reject { |key, value| value.nil? }
24
+ end
25
+
26
+ # Read the casted value of an attribute defined with a property.
27
+ #
28
+ # ==== Returns
29
+ # Object:: the casted attibutes value.
30
+ def read_attribute(property, locale = nil)
31
+ property = "#{property}_#{locale}" if locale
32
+ @properties_data[property.to_s]
33
+ end
34
+
35
+ # Store a casted value in the current instance of an attribute defined
36
+ # with a property and update dirty status
37
+ def write_attribute(property, value, locale = nil)
38
+ # retrieve options for the attribute
39
+ opts = self.class.properties[property.to_s]
40
+
41
+ # Check if we should store a localized version of the data
42
+ property = "#{property}_#{locale}" if locale
43
+
44
+ # TODO: Mark dirty attribute tracking
45
+
46
+ # Cast the value before storing it
47
+ cast_to = opts && opts[:cast_to] || Object
48
+
49
+ @properties_data[property.to_s] = if value.nil?
50
+ nil
51
+ elsif cast_to == String
52
+ value.to_s
53
+ elsif cast_to == Integer
54
+ value.to_i
55
+ elsif cast_to == Float
56
+ value.to_f
57
+ elsif cast_to == TrueClass or cast_to == FalseClass
58
+ if value.kind_of?(Integer)
59
+ value == 1
60
+ else
61
+ %w[ true 1 t ].include?(value.to_s.downcase)
62
+ end
63
+ else
64
+ value
65
+ end
66
+ end
67
+
68
+ def update_attributes_without_saving(hash)
69
+ return if hash.nil?
70
+ hash.each do |key, value|
71
+ if self.respond_to?("#{key}=")
72
+ self.send("#{key}=", value)
73
+ else
74
+ @properties_data[key] = value
75
+ end
76
+ end
77
+ end
78
+ alias :attributes= :update_attributes_without_saving
79
+
80
+
81
+ end
82
+
83
+ module ClassMethods
84
+
85
+ # Allow setting properties
86
+ def property(name, options = {})
87
+ unless self.properties.keys.find { |p| p == name.to_s }
88
+ define_property(name, options)
89
+ end
90
+ end
91
+
92
+ def timestamps!
93
+ property(:updated_at, :cast_to => Time)
94
+ property(:created_at, :cast_to => Time)
95
+
96
+ set_callback :save, :before do |object|
97
+ write_attribute('updated_at', Time.now)
98
+ write_attribute('created_at', Time.now) if object.new?
99
+ end
100
+ end
101
+
102
+ protected
103
+
104
+ # This is not a thread safe operation, if you have to set new properties at runtime
105
+ # make sure a mutex is used.
106
+ def define_property(name, options={})
107
+ # read only flag
108
+ read_only = options.delete(:read_only) || false
109
+
110
+ # Store the property and its options
111
+ self.properties[name.to_s] = options
112
+
113
+ # Create getter and setter methods
114
+ create_property_getter(name)
115
+ create_property_setter(name) unless read_only == true
116
+
117
+ # Return property name just in case ;)
118
+ name
119
+ end
120
+
121
+ # defines the getter for the property (and optional aliases)
122
+ def create_property_getter(name)
123
+ define_method(name) do
124
+ # Get property configuration
125
+ localize = self.class.properties[name.to_s][:localize]
126
+
127
+ # Determine current locale
128
+ locale = localize ? I18n.locale : nil
129
+
130
+ # Fetch property value
131
+ result = read_attribute(name, locale)
132
+
133
+ # If there is a fallback locale, fetch its value if appropriate
134
+ if result.nil? && localize && localize != true && localize.to_s != locale.to_s
135
+ result = read_attribute(name, localize)
136
+ end
137
+
138
+ # Finally return some data
139
+ result
140
+ end
141
+
142
+ opts = self.properties[name.to_s]
143
+
144
+ if opts[:cast_to].presence && opts[:cast_to] == TrueClass
145
+ define_method("#{name}?") do
146
+ value = read_attribute(name)
147
+ !(value.nil? || value == false)
148
+ end
149
+ end
150
+ end
151
+
152
+ # defines the setter for the property (and optional aliases)
153
+ def create_property_setter(name)
154
+ define_method("#{name}=") do |value|
155
+ locale = self.class.properties[name.to_s][:localize] ? I18n.locale : nil
156
+ write_attribute(name, value, locale)
157
+ end
158
+ end
159
+
160
+ end
161
+
162
+ end
163
+
164
+ end
165
+
166
+ end