Class: SafetyPin::Node

Inherits:
Object
  • Object
show all
Defined in:
lib/safety_pin/node.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(j_node) ⇒ Node

Returns a new instance of Node.



89
90
91
# File 'lib/safety_pin/node.rb', line 89

def initialize(j_node)
  @j_node = j_node
end

Instance Attribute Details

#j_nodeObject (readonly)

Returns the value of attribute j_node.



9
10
11
# File 'lib/safety_pin/node.rb', line 9

def j_node
  @j_node
end

Class Method Details

.build(node_blueprint) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/safety_pin/node.rb', line 31

def self.build(node_blueprint)
  raise NodeError.new("NodeBlueprint is nil") if node_blueprint.nil?
  raise NodeError.new("NodeBlueprint has non-absolute path") unless node_blueprint.path.to_s.start_with?("/")
  raise NodeError.new("Node already exists at path: #{node_blueprint.path}") if Node.exists?(node_blueprint.path)
  
  rel_path_to_root_node = node_blueprint.path.to_s[1..-1]
  node = self.new(session.root_node.add_node(rel_path_to_root_node, node_blueprint.primary_type))
  node.properties = node_blueprint.properties

  node
rescue javax.jcr.PathNotFoundException => e
  raise NodeError.new("Cannot add a new node to a non-existing parent at #{node_blueprint.path}")
end

.create(node_blueprint) ⇒ Object



54
55
56
57
58
# File 'lib/safety_pin/node.rb', line 54

def self.create(node_blueprint)
  node = self.build(node_blueprint)
  node.save
  node
end

.create_or_update(node_blueprint_or_node_blueprints) ⇒ Object



78
79
80
81
82
83
84
85
86
87
# File 'lib/safety_pin/node.rb', line 78

def self.create_or_update(node_blueprint_or_node_blueprints)
  node_blueprints = Array(node_blueprint_or_node_blueprints)
  node_blueprints.map do |node_blueprint|
    if exists?(node_blueprint.path)
      update(node_blueprint)
    else
      create(node_blueprint)
    end
  end
end

.create_parents(path) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/safety_pin/node.rb', line 60

def self.create_parents(path)
  intermediate_paths = []

  current_intermediate_path = Pathname(path)
  while(current_intermediate_path.to_s != "/")
    current_intermediate_path = current_intermediate_path.parent
    intermediate_paths.push(current_intermediate_path)
  end
  
  results = intermediate_paths.reverse.map do |intermediate_path|
    create(NodeBlueprint.new(:path => intermediate_path.to_s)) unless exists?(intermediate_path)
  end

  session.save

  results
end

.exists?(path) ⇒ Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/safety_pin/node.rb', line 23

def self.exists?(path)
  find(path) != nil
end

.find(path) ⇒ Object



11
12
13
14
15
16
# File 'lib/safety_pin/node.rb', line 11

def self.find(path)
  raise ArgumentError unless path.to_s.start_with?("/")
  Node.new(session.get_node(path.to_s))
rescue javax.jcr.PathNotFoundException
  nil
end

.find_or_create(path, primary_type = nil) ⇒ Object



18
19
20
21
# File 'lib/safety_pin/node.rb', line 18

def self.find_or_create(path, primary_type = nil)
  node_blueprint = NodeBlueprint.new(:path => path.to_s, :primary_type => primary_type)
  find(path) || create(node_blueprint)
end

.sessionObject



27
28
29
# File 'lib/safety_pin/node.rb', line 27

def self.session
  JCR.session
end

.update(node_blueprint) ⇒ Object



45
46
47
48
49
50
51
52
# File 'lib/safety_pin/node.rb', line 45

def self.update(node_blueprint)
  node = find(node_blueprint.path)
  # raise NodeError.new("Cannot retrieve node for update -- might not exist") if node.nil?
  node.properties = node_blueprint.properties
  node.primary_type = node_blueprint.primary_type
  node.save
  node
end

Instance Method Details

#==(other_node) ⇒ Object



101
102
103
104
105
# File 'lib/safety_pin/node.rb', line 101

def ==(other_node)
  return false if other_node.nil?
  return false unless other_node.respond_to?(:path)
  self.path == other_node.path
end

#[](name) ⇒ Object



223
224
225
# File 'lib/safety_pin/node.rb', line 223

def [](name)
  read_attribute(name)
end

#[]=(name, value) ⇒ Object



227
228
229
# File 'lib/safety_pin/node.rb', line 227

def []=(name, value)
  write_attribute(name, value)
end

#add_mixin(mixin_name) ⇒ Object



322
323
324
# File 'lib/safety_pin/node.rb', line 322

def add_mixin(mixin_name)
  j_node.add_mixin(mixin_name)
end

#build(name, node_blueprint = nil) ⇒ Object



348
349
350
# File 'lib/safety_pin/node.rb', line 348

def build(name, node_blueprint = nil)
  Node.build(node_blueprint_for(name, node_blueprint))
end

#changed?Boolean

Returns:

  • (Boolean)


231
232
233
# File 'lib/safety_pin/node.rb', line 231

def changed?
  j_node.modified?
end

#child(relative_path) ⇒ Object



120
121
122
123
124
125
# File 'lib/safety_pin/node.rb', line 120

def child(relative_path)
  child_j_node = j_node.get_node(relative_path.to_s)
  Node.new(child_j_node)
rescue javax.jcr.PathNotFoundException
  nil
end

#childrenObject



112
113
114
115
116
117
118
# File 'lib/safety_pin/node.rb', line 112

def children
  child_nodes = []
  j_node.get_nodes.each do |child_j_node|
    child_nodes << Node.new(child_j_node)
  end
  child_nodes
end

#convert_hash_to_node_blueprint(hash) ⇒ Object

Convert a hash (and it’s values recursively) to NodeBlueprints. This is a helper method, allowing a hash to be passed in to Node#properties= when only properties need to be set. One caveat: all node types will default to nt:unstructured.



295
296
297
298
299
300
301
302
# File 'lib/safety_pin/node.rb', line 295

def convert_hash_to_node_blueprint(hash)
  hash.keys.each do |key|
    if hash[key].is_a? Hash
      hash[key] = convert_hash_to_node_blueprint(hash[key])
    end
  end
  NodeBlueprint.new(:path => :no_path, :properties => hash)
end

#create(name, node_blueprint = nil) ⇒ Object

Create and return a child node with a given name



344
345
346
# File 'lib/safety_pin/node.rb', line 344

def create(name, node_blueprint = nil)
  Node.create(node_blueprint_for(name, node_blueprint))
end

#destroyObject



308
309
310
311
312
313
314
315
316
# File 'lib/safety_pin/node.rb', line 308

def destroy
  path = self.path
  parent_j_node = j_node.parent
  j_node.remove
  parent_j_node.save
  # raise NodeError.new("Unable to destroy #{path} node") unless self.class.find(path).nil?
rescue javax.jcr.RepositoryException => e
  raise NodeError.new("Unable to destroy #{path} node: #{e.message}")
end

#find_or_create(name, primary_type = nil) ⇒ Object



338
339
340
341
# File 'lib/safety_pin/node.rb', line 338

def find_or_create(name, primary_type = nil)
  path = Pathname(self.path) + name
  self.class.find_or_create(path.to_s, primary_type)
end

#has_property(name) ⇒ Object



384
385
386
# File 'lib/safety_pin/node.rb', line 384

def has_property(name)
  properties.keys.include?(name)
end

#mixin_typesObject



318
319
320
# File 'lib/safety_pin/node.rb', line 318

def mixin_types
  j_node.mixin_node_types.map(&:name)
end

#nameObject



127
128
129
# File 'lib/safety_pin/node.rb', line 127

def name
  @name ||= j_node.name
end

#new?Boolean

Returns:

  • (Boolean)


235
236
237
# File 'lib/safety_pin/node.rb', line 235

def new?
  j_node.new?
end

#node_blueprint_for(name, node_blueprint = nil) ⇒ Object



352
353
354
355
356
357
358
359
360
361
# File 'lib/safety_pin/node.rb', line 352

def node_blueprint_for(name, node_blueprint = nil)
  path = Pathname(self.path) + name.to_s
  
  unless node_blueprint.nil?
    properties = node_blueprint.properties
    primary_type = node_blueprint.primary_type
  end
  
  NodeBlueprint.new(:path => path.to_s, :properties => properties, :primary_type => primary_type)
end

#parentObject

Raises:



107
108
109
110
# File 'lib/safety_pin/node.rb', line 107

def parent
  raise NodeError.new("Root node does not have parent") if path == "/"
  Node.new(j_node.parent)
end

#pathObject



93
94
95
# File 'lib/safety_pin/node.rb', line 93

def path
  @path ||= j_node.path
end

#primary_typeObject



330
331
332
# File 'lib/safety_pin/node.rb', line 330

def primary_type
  self["jcr:primaryType"]
end

#primary_type=(primary_type) ⇒ Object



334
335
336
# File 'lib/safety_pin/node.rb', line 334

def primary_type=(primary_type)
  j_node.set_primary_type(primary_type)
end

#propertiesObject



239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/safety_pin/node.rb', line 239

def properties
  props = {}
  prop_iter = j_node.properties
  while prop_iter.has_next
    prop = prop_iter.next_property        
    unless prop.definition.protected?
      prop_name = prop.name
      props[prop_name] = self[prop_name]
    end
  end
  props
end

#properties=(new_props) ⇒ Object



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/safety_pin/node.rb', line 265

def properties=(new_props)
  property_names = (properties.keys + new_props.keys).uniq
  property_names.each do |name|
    # REFACTOR ME PLZ
    child_path = Pathname(path.to_s) + name.to_s
    if new_props[name].is_a? Hash
      new_props[name] = convert_hash_to_node_blueprint(new_props[name])
    end

    if new_props[name].respond_to?(:node_blueprint?) and new_props[name].node_blueprint?
      # Handle node blue prints
      node_blueprint = NodeBlueprint.new(:properties => new_props[name].properties, 
                                         :path => child_path.to_s, 
                                         :primary_type => new_props[name].primary_type)
      if Node.exists?(child_path)
        Node.update(node_blueprint)
      else
        Node.build(node_blueprint)
      end
    else
      # handle everything else
      self[name] = new_props[name]
    end
  end
end

#property_is_multi_valued?(property) ⇒ Boolean

Returns:

  • (Boolean)


143
144
145
146
147
148
# File 'lib/safety_pin/node.rb', line 143

def property_is_multi_valued?(property)
  property.values
  true
rescue javax.jcr.ValueFormatException
  false
end

#protected_propertiesObject



252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/safety_pin/node.rb', line 252

def protected_properties
  props = {}
  prop_iter = j_node.properties
  while prop_iter.has_next
    prop = prop_iter.next_property        
    if prop.definition.protected?
      prop_name = prop.name
      props[prop_name] = self[prop_name]
    end
  end
  props
end

#read_attribute(name) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/safety_pin/node.rb', line 131

def read_attribute(name)
  name = name.to_s
  property = j_node.get_property(name)
  if property_is_multi_valued?(property)
    retrieve_property_multi_value(property)
  else
    retrieve_property_value(property)
  end
rescue javax.jcr.PathNotFoundException
  raise NilPropertyError.new("#{name} property not found on node")
end

#reloadObject



219
220
221
# File 'lib/safety_pin/node.rb', line 219

def reload
  j_node.refresh(false)
end

#remove_mixin(mixin_name) ⇒ Object



326
327
328
# File 'lib/safety_pin/node.rb', line 326

def remove_mixin(mixin_name)
  j_node.remove_mixin(mixin_name)
end

#replace_property(opts, &block) ⇒ Object



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/safety_pin/node.rb', line 363

def replace_property(opts, &block)
  opts = {recursive: false}.merge(opts)
  name_pattern = opts.fetch(:name)
  target_pattern = opts.fetch(:target)
  replacement_block = block || lambda {|value| opts.fetch(:replacement) }

  modified = false
  properties.each do |name, value|
    if name.match(name_pattern) and value.match(target_pattern)
      self[name] = replacement_block.call(value)
      modified = true
    end
  end

  modified_nodes = []
  modified_nodes << self if modified
  modified_nodes << children.map {|child_node| child_node.replace_property(opts) } if opts.fetch(:recursive)

  modified_nodes.flatten
end

#retrieve_property_multi_value(property) ⇒ Object



150
151
152
# File 'lib/safety_pin/node.rb', line 150

def retrieve_property_multi_value(property)
  property.values.map {|value| retrieve_value(value) }
end

#retrieve_property_value(property) ⇒ Object



154
155
156
# File 'lib/safety_pin/node.rb', line 154

def retrieve_property_value(property)
  retrieve_value(property.value)
end

#retrieve_value(value) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/safety_pin/node.rb', line 158

def retrieve_value(value)
  property_type = PropertyType.name_from_value(value.type)
  case property_type
  when "String"
    value.string
  when "Boolean"
    value.boolean
  when "Double"
    value.double
  when "Long"
    value.long
  when "Date"
    Time.at(value.date.time.time / 1000)
  when "Name"
    value.string # Not sure if these should be handled differently
  else
    raise PropertyTypeError.new("Unknown property type: #{property_type}")
  end
end

#saveObject



209
210
211
212
213
214
215
216
217
# File 'lib/safety_pin/node.rb', line 209

def save
  if new?
    j_node.parent.save
  else
    j_node.save
  end
  
  not changed?
end

#sessionObject



97
98
99
# File 'lib/safety_pin/node.rb', line 97

def session
  @session ||= JCR.session
end

#value_factoryObject



304
305
306
# File 'lib/safety_pin/node.rb', line 304

def value_factory
  session.value_factory
end

#write_attribute(name, value) ⇒ Object

Raises:



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/safety_pin/node.rb', line 178

def write_attribute(name, value)
  raise PropertyError.new("Illegal operation: cannot change jcr:primaryType property") if name == "jcr:primaryType"
  name = name.to_s
  
  if value.nil? and not j_node.has_property(name)
    return nil
  end
  
  if value.is_a? Array
    values = value
    val_fact = value_factory
    j_values = []
    values.each do |value|
      j_values << val_fact.create_value(value.to_java)
    end
    j_node.set_property(name, j_values.to_java(Java::JavaxJcr::Value))
  elsif value.is_a? Time or value.is_a? Date
    calendar_value = Calendar.instance
    calendar_value.set_time(value.to_java)
    j_node.set_property(name, calendar_value)
  elsif value.is_a? Symbol
    j_node.set_property(name, value.to_s)
  else
    begin
      j_node.set_property(name, value)
    rescue NameError
      raise SafetyPin::PropertyTypeError.new("Property value type of #{value.class} is unsupported")
    end
  end
end