Skip to content

Commit

Permalink
Merge pull request #660 from neo4jrb/create_unique
Browse files Browse the repository at this point in the history
adds unique: true, creates_unique_rel to ActiveNode and ActiveRel
  • Loading branch information
subvertallchris committed Jan 7, 2015
2 parents 616ded4 + 6415ad4 commit c11d958
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 7 deletions.
13 changes: 11 additions & 2 deletions lib/neo4j/active_node/has_n/association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ def inject_classname(properties)
properties
end

def unique?
@origin ? origin_association.unique? : !!@unique
end

private

def get_direction(relationship_cypher, create)
Expand All @@ -131,8 +135,12 @@ def get_properties_string(properties)
p.size == 0 ? '' : " {#{p}}"
end

def origin_association
target_class.associations[@origin]
end

def origin_type
target_class.associations[@origin].relationship_type
origin_association.relationship_type
end

private
Expand All @@ -144,6 +152,7 @@ def apply_vars_from_options(options)
@relationship_class = options[:rel_class]
@relationship_type = options[:type] && options[:type].to_sym
@dependent = options[:dependent]
@unique = options[:unique]
end

# Return basic details about association as declared in the model
Expand Down Expand Up @@ -190,7 +199,7 @@ def validate_origin!

fail ArgumentError, 'Cannot use :origin without a model_class (implied or explicit)' if not target_class

association = target_class.associations[@origin]
association = origin_association
fail ArgumentError, "Origin `#{@origin.inspect}` association not found for #{target_class} (specified in #{base_declaration})" if not association

fail ArgumentError, "Origin `#{@origin.inspect}` (specified in #{base_declaration}) has same direction `#{@direction}`)" if @direction == association.direction
Expand Down
6 changes: 5 additions & 1 deletion lib/neo4j/active_node/query/query_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def create(other_nodes, properties)
_session.query(context: @options[:context])
.match("(start#{match_string(start_object)}), (end#{match_string(other_node)})").where('ID(start) = {start_id} AND ID(end) = {end_id}')
.params(start_id: start_object.neo_id, end_id: other_node.neo_id)
.create("start#{_association_arrow(properties, true)}end").exec
.send(create_method, "start#{_association_arrow(properties, true)}end").exec

@association.perform_callback(@options[:start_object], other_node, :after)
# end
Expand Down Expand Up @@ -283,6 +283,10 @@ def _match_type

private

def create_method
association.unique? ? :create_unique : :create
end

def build_deeper_query_proxy(method, args)
self.dup.tap do |new_query|
args.each do |arg|
Expand Down
10 changes: 7 additions & 3 deletions lib/neo4j/active_rel/persistence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,15 @@ def _rel_creation_query(from_node, to_node, props)
.where("n1.#{from_class.primary_key} = {from_node_id}")
.where("n2.#{to_class.primary_key} = {to_node_id}")
.params(from_node_id: from_node.id, to_node_id: to_node.id)
.create("(n1)-[r:`#{type}`]->(n2)")
.send(create_method, ("(n1)-[r:`#{type}`]->(n2)"))
.with('r').set(r: props).return(:r).first.r
rescue NoMethodError
raise RelCreateFailedError, "Unable to create relationship. from_node: #{from_node}, to_node: #{to_node}"
rescue NoMethodError => e
raise RelCreateFailedError, "Unable to create relationship. from_node: #{from_node}, to_node: #{to_node}, error: #{e}"
end
end

def create_method
self.class.unique? ? :create_unique : :create
end
end
end
8 changes: 8 additions & 0 deletions lib/neo4j/active_rel/property.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ def extract_association_attributes!(attributes)
def load_entity(id)
Neo4j::Node.load(id)
end

def creates_unique_rel
@unique = true
end

def unique?
!!@unique
end
end

private
Expand Down
16 changes: 16 additions & 0 deletions spec/e2e/active_rel_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ class MyRelClass
expect(from_node).to receive(:id).at_least(1).times.and_return(nil)
expect { MyRelClass.create(from_node: from_node, to_node: to_node) }.to raise_error Neo4j::ActiveRel::Persistence::RelCreateFailedError
end

describe 'creates_unique_rel' do
after do
MyRelClass.instance_variable_set(:@unique, false)
[from_node, to_node].each(&:destroy)
end

it 'creates a unique relationship between to nodes' do
expect(from_node.others.count).to eq 0
MyRelClass.create(from_node: from_node, to_node: to_node)
expect(from_node.others.count).to eq 1
MyRelClass.creates_unique_rel
MyRelClass.create(from_node: from_node, to_node: to_node)
expect(from_node.others.count).to eq 1
end
end
end

describe 'properties' do
Expand Down
26 changes: 25 additions & 1 deletion spec/e2e/has_many_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require 'spec_helper'

describe 'has_n' do
describe 'has_many' do
let(:clazz_a) do
UniqueClass.create do
include Neo4j::ActiveNode
Expand Down Expand Up @@ -39,6 +39,30 @@
end
end

describe 'unique: true' do
before { clazz_a.reflect_on_association(:knows).association.instance_variable_set(:@unique, true) }
after do
clazz_a.reflect_on_association(:knows).association.instance_variable_set(:@unique, false)
[friend1, friend2].each(&:destroy)
end

it 'only creates one relationship between two nodes' do
expect(friend1.knows.count).to eq 0
friend1.knows << friend2
expect(friend1.knows.count).to eq 1
friend1.knows << friend2
expect(friend1.knows.count).to eq 1
end

it 'is respected with an association using origin' do
expect(friend1.knows.count).to eq 0
friend2.knows_me << friend1
expect(friend1.knows.count).to eq 1
friend2.knows_me << friend1
expect(friend1.knows.count).to eq 1
end
end

describe 'rel_type' do
it 'creates the correct type' do
node.friends << friend1
Expand Down
14 changes: 14 additions & 0 deletions spec/unit/association_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -183,5 +183,19 @@ def self._type
expect(association.send(:relationship_class)).to eq :foo
end
end

describe 'unique' do
context 'true' do
let(:options) { {unique: true} }

it { expect(subject).to be_unique }
end

context 'false' do
let(:options) { {unique: false} }

it { expect(subject).not_to be_unique }
end
end
end
end

0 comments on commit c11d958

Please sign in to comment.