Skip to content

Commit

Permalink
Using the @option tag to document options
Browse files Browse the repository at this point in the history
typo fix

Rubocop

Extracting Cypher-related methods into their own modules

Testing the different possible values for the `:rel_length` option and the generated Cypher when used with other relationship options

End-to-end tests for has_many queries with variable-length relationships

End-to-end tests for the has_one association

Accept and transmit :rel_length option when querying an association

* The association proxy cache should use queried :rel_length as a cache key to avoid returning incorrect results for queries that differ only by the given :rel_length value
* `has_one` method now accepts options and, depending on the presence and type of the :rel_length option, will either return a collection or a single node

This is the actual code that validates and makes use of the :rel_length option to allow variable-length relationships queries

Updating tests to allow for a Hash representation of relationships variable-length qualifiers: {min: 0, max: 2} will be translated to `*0..2` whereas omitting `:min` or `:max` will simply translate to "open-ended" ranges.

Implement changes to the variable-length relationships feature to allow for a Hash representation of ranges, thus providing an easy way to specify open-ended bounds.
Consequently, the `:none_or_more` Symbol has been removed as it felt more like a "patch" than part of the feature

I have taken the liberty to increase (+1) the maximum allowed Cyclomatic complexity as, to me, there's no satisfying way to reduce the Cyclomatic complexity of AssociationCypherMethods#validate_rel_length

Updating tests to allow for a single parameter (an options hash) in calls to `has_(one|many)` methods
(+ fixed rubocop offense)

Implementation for tests allowing the use of a single argument in calls to `has_(one|many)` getter methods

Typo

Adding doc about queries with variable-length relationships

An additional note on variable-length rels with `has_one` associations

Docs: reference tutorial doc on : rel_length option

Removing maximum allowed Cyclomatic Complexity increase

* Refactoring rel_length validation to call `fail` from a single point and only create error messages in the other methods.
* Most checks have been extracted to their own methods to bring the cyclomatic and perceived complexity of each method below the default accepted maximums

Fixed tests to match new error messages from refactoring

Updated doc files
  • Loading branch information
olance committed Jun 30, 2015
1 parent d1f928c commit a959a42
Show file tree
Hide file tree
Showing 26 changed files with 717 additions and 149 deletions.
2 changes: 0 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,3 @@ Style/Lambda:
# Reason: I'm proud to be part of the double negative Ruby tradition
Style/DoubleNegation:
Enabled: false


67 changes: 63 additions & 4 deletions docs/Querying.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Here we are limiting lessons by the ``start_date`` and ``end_date`` on the relat
student.lessons.where(subject: 'Math').rel_where(grade: 85)
Paramaters
Parameters
~~~~~~~~~~

If you need to use a string in where, you should set the parameter manually.
Expand All @@ -87,6 +87,65 @@ If you need to use a string in where, you should set the parameter manually.
.params(age: params[:age], name: params[:name], home_town: params[:home_town])
.pluck(:s)
Variable-length relationships
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

It is possible to specify a variable-length qualifier to apply to relationships when calling association methods.

.. code-block:: ruby
student.friends(rel_length: 2)
This would find the friends of friends of a student. Note that you can still name matched nodes and relationships and use those names to build your query as seen above:

.. code-block:: ruby
student.friends(:f, :r, rel_length: 2).where('f.gender = {gender} AND r.since >= {date}').params(gender: 'M', date: 1.month.ago)
.. note::

You can either pass a single options Hash or provide **both** the node and relationship names along with the optional Hash.


There are many ways to provide the length information to generate all the various possibilities Cypher offers:

.. code-block:: ruby
# As a Fixnum:
## Cypher: -[:`FRIENDS`*2]->
student.friends(rel_length: 2)
# As a Range:
## Cypher: -[:`FRIENDS`*1..3]->
student.friends(rel_length: 1..3) # Get up to 3rd degree friends
# As a Hash:
## Cypher: -[:`FRIENDS`*1..3]->
student.friends(rel_length: {min: 1, max: 3})
## Cypher: -[:`FRIENDS`*0..]->
student.friends(rel_length: {min: 0})
## Cypher: -[:`FRIENDS`*..3]->
student.friends(rel_length: {max: 3})
# As the :any Symbol:
## Cypher: -[:`FRIENDS`*]->
student.friends(rel_length: :any)
.. caution::
By default, "\*..3" is equivalent to "\*1..3" and "\*" is equivalent to "\*1..", but this may change
depending on your Node4j server configuration. Keep that in mind when using variable-length
relationships queries without specifying a minimum value.


.. note::
When using variable-length relationships queries on `has_one` associations, be aware that multiple nodes
could be returned!


The Query API
-------------

Expand Down Expand Up @@ -150,7 +209,7 @@ Find or Create By...
QueryProxy has a ``find_or_create_by`` method to make the node rel creation process easier. Its usage is simple:

.. code-block:: ruby
a_node.an_association(params_hash)
The method has branching logic that attempts to match an existing node and relationship. If the pattern is not found, it tries to find a node of the expected class and create the relationship. If *that* doesn't work, it creates the node, then creates the relationship. The process is wrapped in a transaction to prevent a failure from leaving the database in an inconsistent state.
Expand All @@ -160,12 +219,12 @@ There are some mild caveats. First, it will not work on associations of class me
.. code-block:: ruby
student.friends.lessons.find_or_create_by(subject: 'Math')
Assuming the ``lessons`` association points to a ``Lesson`` model, you would effectively end up with this:

.. code-block:: ruby
math = Lesson.find_or_create_by(subject: 'Math')
student.friends.lessons << math
...which is invalid and will result in an error.
32 changes: 17 additions & 15 deletions docs/api/Neo4j.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ Neo4j
:titlesonly:


Neo4j/Config

Neo4j/Shared

Neo4j/Neo4jrbError

Neo4j/RecordNotFound


Neo4j/Shared

Neo4j/Railtie
Neo4j/Config



Neo4j/ClassWrapper

Neo4j/Railtie

Neo4j/Paginated

Neo4j/Migration
Expand Down Expand Up @@ -60,18 +60,18 @@ Files



* `lib/neo4j/config.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/config.rb#L1>`_
* `lib/neo4j/errors.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/errors.rb#L1>`_

* `lib/neo4j/shared.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/shared.rb#L1>`_

* `lib/neo4j/errors.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/errors.rb#L1>`_
* `lib/neo4j/config.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/config.rb#L1>`_

* `lib/neo4j/version.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/version.rb#L1>`_

* `lib/neo4j/railtie.rb:4 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/railtie.rb#L4>`_

* `lib/neo4j/wrapper.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/wrapper.rb#L1>`_

* `lib/neo4j/railtie.rb:4 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/railtie.rb#L4>`_

* `lib/neo4j/paginated.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/paginated.rb#L1>`_

* `lib/neo4j/migration.rb:3 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/migration.rb#L3>`_
Expand All @@ -90,10 +90,10 @@ Files

* `lib/neo4j/shared/typecaster.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/shared/typecaster.rb#L1>`_

* `lib/neo4j/shared/validations.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/shared/validations.rb#L1>`_

* `lib/neo4j/active_node/labels.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/labels.rb#L1>`_

* `lib/neo4j/shared/validations.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/shared/validations.rb#L1>`_

* `lib/neo4j/active_rel/callbacks.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_rel/callbacks.rb#L1>`_

* `lib/neo4j/active_node/callbacks.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/callbacks.rb#L1>`_
Expand All @@ -102,10 +102,10 @@ Files

* `lib/neo4j/active_rel/validations.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_rel/validations.rb#L1>`_

* `lib/neo4j/active_node/orm_adapter.rb:3 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/orm_adapter.rb#L3>`_

* `lib/neo4j/active_node/validations.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/validations.rb#L1>`_

* `lib/neo4j/active_node/orm_adapter.rb:3 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/orm_adapter.rb#L3>`_

* `lib/neo4j/active_node/query_methods.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/query_methods.rb#L1>`_

* `lib/rails/generators/neo4j_generator.rb:5 <https://github.com/neo4jrb/neo4j/blob/master/lib/rails/generators/neo4j_generator.rb#L5>`_
Expand All @@ -120,9 +120,11 @@ Files

* `lib/neo4j/active_node/query/query_proxy_enumerable.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/query/query_proxy_enumerable.rb#L1>`_

* `lib/neo4j/active_node/dependent/association_methods.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/dependent/association_methods.rb#L1>`_

* `lib/neo4j/active_node/dependent/query_proxy_methods.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/dependent/query_proxy_methods.rb#L1>`_

* `lib/neo4j/active_node/dependent/association_methods.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/dependent/association_methods.rb#L1>`_
* `lib/neo4j/active_node/has_n/association_cypher_methods.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/has_n/association_cypher_methods.rb#L1>`_

* `lib/neo4j/active_node/query/query_proxy_find_in_batches.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb#L1>`_

Expand Down
22 changes: 12 additions & 10 deletions docs/api/Neo4j/ActiveNode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,18 @@ in a new object of that class.

ActiveNode/Dependent

ActiveNode/Reflection

ActiveNode/Initialize

ActiveNode/ClassMethods

ActiveNode/OrmAdapter
ActiveNode/Reflection

ActiveNode/IdProperty

ActiveNode/Validations

ActiveNode/ClassMethods

ActiveNode/OrmAdapter

ActiveNode/Persistence

ActiveNode/QueryMethods
Expand Down Expand Up @@ -98,12 +98,12 @@ Files

* `lib/neo4j/active_node/reflection.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/reflection.rb#L1>`_

* `lib/neo4j/active_node/orm_adapter.rb:4 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/orm_adapter.rb#L4>`_

* `lib/neo4j/active_node/id_property.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/id_property.rb#L1>`_

* `lib/neo4j/active_node/validations.rb:2 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/validations.rb#L2>`_

* `lib/neo4j/active_node/orm_adapter.rb:4 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/orm_adapter.rb#L4>`_

* `lib/neo4j/active_node/persistence.rb:1 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/persistence.rb#L1>`_

* `lib/neo4j/active_node/query_methods.rb:2 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/query_methods.rb#L2>`_
Expand All @@ -118,9 +118,11 @@ Files

* `lib/neo4j/active_node/query/query_proxy_enumerable.rb:2 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/query/query_proxy_enumerable.rb#L2>`_

* `lib/neo4j/active_node/dependent/association_methods.rb:2 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/dependent/association_methods.rb#L2>`_

* `lib/neo4j/active_node/dependent/query_proxy_methods.rb:2 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/dependent/query_proxy_methods.rb#L2>`_

* `lib/neo4j/active_node/dependent/association_methods.rb:2 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/dependent/association_methods.rb#L2>`_
* `lib/neo4j/active_node/has_n/association_cypher_methods.rb:2 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/has_n/association_cypher_methods.rb#L2>`_

* `lib/neo4j/active_node/query/query_proxy_find_in_batches.rb:2 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb#L2>`_

Expand Down Expand Up @@ -258,7 +260,7 @@ Methods

def association_proxy(name, options = {})
name = name.to_sym
hash = [name, options.values_at(:node, :rel, :labels)].hash
hash = [name, options.values_at(:node, :rel, :labels, :rel_length)].hash
association_proxy_cache_fetch(hash) do
if previous_association_proxy = self.instance_variable_get('@association_proxy')
result_by_previous_id = previous_association_proxy_results_by_previous_id(previous_association_proxy, name)
Expand Down Expand Up @@ -978,7 +980,7 @@ Methods
.. hidden-code-block:: ruby

def valid?(context = nil)
context ||= (new_record? ? :create : :update)
context ||= (new_record? ? :create : :update)
super(context)
errors.empty?
end
Expand Down
8 changes: 4 additions & 4 deletions docs/api/Neo4j/ActiveNode/Dependent.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ Dependent



Dependent/QueryProxyMethods

Dependent/AssociationMethods

Dependent/QueryProxyMethods




Expand All @@ -36,10 +36,10 @@ Files

* `lib/neo4j/active_node/dependent.rb:3 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/dependent.rb#L3>`_

* `lib/neo4j/active_node/dependent/query_proxy_methods.rb:3 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/dependent/query_proxy_methods.rb#L3>`_

* `lib/neo4j/active_node/dependent/association_methods.rb:3 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/dependent/association_methods.rb#L3>`_

* `lib/neo4j/active_node/dependent/query_proxy_methods.rb:3 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/dependent/query_proxy_methods.rb#L3>`_




Expand Down
6 changes: 5 additions & 1 deletion docs/api/Neo4j/ActiveNode/HasN.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ HasN

HasN/Association

HasN/AssociationCypherMethods




Expand All @@ -54,6 +56,8 @@ Files

* `lib/neo4j/active_node/has_n/association.rb:5 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/has_n/association.rb#L5>`_

* `lib/neo4j/active_node/has_n/association_cypher_methods.rb:3 <https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/active_node/has_n/association_cypher_methods.rb#L3>`_




Expand All @@ -72,7 +76,7 @@ Methods

def association_proxy(name, options = {})
name = name.to_sym
hash = [name, options.values_at(:node, :rel, :labels)].hash
hash = [name, options.values_at(:node, :rel, :labels, :rel_length)].hash
association_proxy_cache_fetch(hash) do
if previous_association_proxy = self.instance_variable_get('@association_proxy')
result_by_previous_id = previous_association_proxy_results_by_previous_id(previous_association_proxy, name)
Expand Down
Loading

0 comments on commit a959a42

Please sign in to comment.