Skip to content

Commit

Permalink
Merge branch 'orm_adapter' into devise-neo4j
Browse files Browse the repository at this point in the history
  • Loading branch information
cheerfulstoic committed May 23, 2014
2 parents 0239927 + 57ad634 commit 2f71146
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 28 deletions.
50 changes: 45 additions & 5 deletions lib/neo4j/active_node/labels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Labels
extend ActiveSupport::Concern

WRAPPED_CLASSES = []
class InvalidQueryError < StandardError; end

# @return the labels
# @see Neo4j-core
Expand Down Expand Up @@ -62,7 +63,7 @@ module ClassMethods
# @param [Hash, nil] args the search critera or nil if finding all
# @param [Neo4j::Session] session defaults to the model's session
def all(args = nil, session = self.neo4j_session)
if (args)
if args
find_by_hash(args, session)
else
Neo4j::Label.find_all_nodes(mapped_label_name, session)
Expand Down Expand Up @@ -128,10 +129,49 @@ def indexed_labels

protected

def find_by_hash(hash, session)
# Not happy with this solution. Would like to see hash format made into something like {conditions: , order: } but even better to have Arel syntax
order = hash.delete(:order)
Neo4j::Label.query(mapped_label_name, {conditions: hash, order: order}, session)
def find_by_hash(query, session)
validate_query!(query)

extract_relationship_conditions!(query)

Neo4j::Label.query(mapped_label_name, query, session)
end

# Raises an error if query is malformed
def validate_query!(query)
invalid_query_keys = query.keys.map(&:to_sym) - [:conditions, :order, :limit, :offset, :skip]

raise InvalidQueryError, "Invalid query keys: #{invalid_query_keys.join(', ')}" if not invalid_query_keys.empty?
end

# Takes out :conditions query keys for associations and creates corresponding :conditions and :matches keys
# example:
# class Person
# property :name
# has_n :friend
# end
#
# :conditions => {name: 'Fred', friend: person}
# should result in:
# :conditions => {name => 'Fred', 'id(n1)' => person.id}, :matches => 'n--n1'
#
def extract_relationship_conditions!(query)
node_num = 1
if query[:conditions]
query[:conditions].dup.each do |key, value|
if has_relationship?(key)
neo_id = value.try(:neo_id) || value
raise InvalidQueryError, "Invalid value for '#{key}' condition" if not neo_id.is_a?(Integer)

query[:matches] ||= []
n_string = "n#{node_num}"
query[:matches] << "n--(#{n_string})"
query[:conditions]["id(#{n_string})"] = neo_id
query[:conditions].delete(key)
node_num += 1
end
end
end
end

def _index(property)
Expand Down
22 changes: 13 additions & 9 deletions lib/neo4j/active_node/orm_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ def get(id)
def find_first(options = {})
conditions, order = extract_conditions!(options)
extract_id!(conditions)
order = hasherize_order(order)

if !order.empty?
find_with_order(conditions, order).to_a.first
klass.find(conditions: conditions, order: order)
else
result = klass.find(conditions)
result = klass.find(conditions: conditions)
result
end
end
Expand All @@ -43,10 +45,12 @@ def find_first(options = {})
def find_all(options = {})
conditions, order, limit, offset = extract_conditions!(options)
extract_id!(conditions)
order = hasherize_order(order)

result = if !order.empty?
find_with_order(conditions, order)
klass.all(conditions: conditions, order: order, limit: limit, offset: offset)
else
klass.all(conditions)
klass.all(conditions: conditions)
end

if limit && offset
Expand All @@ -70,16 +74,16 @@ def destroy(object)

private

def hasherize_order(order)
(order || []).map {|clause| Hash[*clause] }
end

def extract_id!(conditions)
if id = conditions.delete(:id)
conditions[:neo_id] = id
conditions['id(n)'] = id
end
end

def find_with_order(conditions, order)
result = klass.all(conditions.merge(order: order.map {|clause| Hash[*clause] }))
end

end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/neo4j/active_node/persistence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def create(props = {})
new(props).tap do |obj|
obj.save
relationship_props.each do |prop, value|
o.send("#{prop}=", value)
obj.send("#{prop}=", value)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/neo4j/active_node/validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def validate_each(record, attribute, value)

# prevent that same object is returned
# TODO: add negative condtion to not return current record
found = @klass.all(conditions).to_a
found = @klass.all(conditions: conditions).to_a
found.delete(record)

if found.count > 0
Expand Down
4 changes: 2 additions & 2 deletions spec/e2e/active_nodel_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def mark_saved

#it { subject.id.should == subject.class.find(flavour: 'vanilla').id}

it { should == subject.class.find(flavour: 'vanilla') }
it { should == subject.class.find(conditions: {flavour: 'vanilla'}) }

it "should be able to modify one of its named attributes" do
lambda{ subject.update_attributes!(:flavour => 'horse') }.should_not raise_error
Expand All @@ -74,7 +74,7 @@ def mark_saved
end

it "should respond to class#all(:flavour => 'vanilla')" do
subject.class.all(flavour: 'vanilla').should include(subject)
subject.class.all(conditions: {flavour: 'vanilla'}).should include(subject)
end

context "and then made invalid" do
Expand Down
10 changes: 5 additions & 5 deletions spec/e2e/inheritance_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ class Car < Vehicle
describe 'find' do
it 'can find using subclass index' do
@volvo.labels.should =~ [:'InheritanceTest::Car', :'InheritanceTest::Node', :'InheritanceTest::Vehicle']
InheritanceTest::Car.find(name: 'volvo').should eq(@volvo)
InheritanceTest::Vehicle.find(name: 'volvo').should eq(@volvo)
InheritanceTest::Car.find(conditions: {name: 'volvo'}).should eq(@volvo)
InheritanceTest::Vehicle.find(conditions: {name: 'volvo'}).should eq(@volvo)
end

it 'can find using baseclass index' do
@saab.labels.should =~ [:'InheritanceTest::Car', :'InheritanceTest::Node', :'InheritanceTest::Vehicle']
InheritanceTest::Car.find(model: '900').should eq(@saab)
InheritanceTest::Vehicle.find(model: '900').should eq(@saab)
InheritanceTest::Car.find(conditions: {model: '900'}).should eq(@saab)
InheritanceTest::Vehicle.find(conditions: {model: '900'}).should eq(@saab)
end

end
Expand All @@ -49,4 +49,4 @@ class Car < Vehicle
InheritanceTest::Car.all.to_a.should =~ [@saab, @volvo]
end
end
end
end
3 changes: 2 additions & 1 deletion spec/e2e/label_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@
end

end
end

end
31 changes: 27 additions & 4 deletions spec/integration/label_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ class SomeLabelClass
include Neo4j::ActiveNode
include SomeLabelMixin
end

class RelationTestClass
include Neo4j::ActiveNode

has_one(:test_class)
end
end


Expand Down Expand Up @@ -89,25 +95,42 @@ class SomeLabelClass
TestClass.all.to_a.should include(p)
end

it 'raises an erorr when not passed a valid query key' do
expect { IndexedTestClass.find(unknown: 'test') }.to raise_error(Neo4j::ActiveNode::Labels::InvalidQueryError)
end

describe 'when indexed' do
it 'can find it using the index' do
IndexedTestClass.destroy_all
kalle = IndexedTestClass.create(name: 'kalle')
IndexedTestClass.find(name: 'kalle').should == kalle
IndexedTestClass.find(conditions: {name: 'kalle'}).should == kalle
end

it 'does not find it if deleted' do
IndexedTestClass.destroy_all
kalle2 = IndexedTestClass.create(name: 'kalle2')
result = IndexedTestClass.find(name: 'kalle2')
result = IndexedTestClass.find(conditions: {name: 'kalle2'})
result.should == kalle2
kalle2.destroy
IndexedTestClass.all(name: 'kalle2').should_not include(kalle2)
IndexedTestClass.all(conditions: {name: 'kalle2'}).should_not include(kalle2)
end
end

describe 'when finding using a Module' do
context 'a relationship' do
let!(:n1) { TestClass.create }
let!(:n2) { RelationTestClass.create(test_class: n1) }

it 'finds when association matches' do
RelationTestClass.find(conditions: {test_class: n1}).should == n2
end

it 'does not find when association does not match' do
RelationTestClass.find(conditions: {test_class: n2}).should be_nil
end

end

describe 'when finding using a Module' do
it 'finds it' do
thing = SomeLabelClass.create
SomeLabelMixin.all.should include(thing)
Expand Down

0 comments on commit 2f71146

Please sign in to comment.