Skip to content

Commit

Permalink
Merge branch 'master' into association_ids
Browse files Browse the repository at this point in the history
Conflicts:
	CHANGELOG.md
  • Loading branch information
cheerfulstoic committed Aug 3, 2015
2 parents cf8613b + 652a455 commit f0de925
Show file tree
Hide file tree
Showing 44 changed files with 2,592 additions and 195 deletions.
2 changes: 2 additions & 0 deletions .overcommit.yml
@@ -1,9 +1,11 @@

PreCommit:
Rubocop:
enabled: true
on_warn: fail # Treat all warnings as failures

TrailingWhitespace:
enabled: true
exclude:
- '**/db/structure.sql' # Ignore trailing whitespace in generated files

Expand Down
2 changes: 1 addition & 1 deletion .rubocop_todo.yml
Expand Up @@ -16,7 +16,7 @@ Metrics/AbcSize:
# Offense count: 2
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 208
Max: 211

# Offense count: 768
# Configuration parameters: AllowURI, URISchemes.
Expand Down
20 changes: 17 additions & 3 deletions CHANGELOG.md
Expand Up @@ -5,9 +5,6 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased][unreleased]

### Changed
- `ActiveNode#destroyed?` and `ActiveRel#destroyed?` now only consider the in-memory state of if an object is destroyed without checking the database

### Fixed
- Added a `before_remove_const` method to clear cached models when Rails `reload!` is called. 5.0.1 included a workaround but this appears to cut to the core of the issue. See https://github.com/neo4jrb/neo4j/pull/855.
- To prevent errors, changing an index to constraint or constraint to index will drop the existing index/constraint before adding the new.
Expand All @@ -19,6 +16,23 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Improved handling of unpersisted nodes with associations. You can now use `<<` to create associations between unpersisted nodes. A `save` will cascade through unpersisted objects, creating nodes and rels along the way. See https://github.com/neo4jrb/neo4j/pull/871
- Support formatted cypher queries for easy reading by humans via the `pretty_logged_cypher_queries` configuration variable
- Ability to query for just IDs on associations
- On `QueryProxy` objects you can now use an `:id` key in `where` and `find_by` methods to refer to the property from `id_property` (`uuid` by default)

## [5.0.10] - 2015-07-31
- Fix what should have been a very obvious bug in `_active_record_destroyed_behavior` behavior
- Add eager loading to QueryProxy so that it works in all expected places

## [5.0.9] - 2015-07-29
- Fix "NameError: uninitialized constant Class::Date" (https://github.com/neo4jrb/neo4j/issues/852)

## [5.0.8] - 2015-07-26
- Copied QueryClauseMethods doc from master

## [5.0.7] - 2015-07-26
- Copied `docs` folder from master because a lot of work had gone into the docs since 5.0.0 was released

## [5.0.6] - 2015-07-22
- Fix query logging so that by default it only outputs to the user in the console and development server. Logger can be changed with `neo4j.config.logger` configuration option

## [5.0.5] - 2015-07-19

Expand Down
2 changes: 1 addition & 1 deletion Rakefile
@@ -1,7 +1,7 @@
require 'rake'
require 'bundler/gem_tasks'
require 'neo4j-core'
load 'neo4j/tasks/neo4j_server.rake'
require 'neo4j/rake_tasks'
load 'neo4j/tasks/migration.rake'

desc 'Generate YARD documentation'
Expand Down
39 changes: 38 additions & 1 deletion docs/ActiveNode.rst
Expand Up @@ -83,6 +83,40 @@ You can declare that a property should have a unique value.
Notice an unique validation is not enough to be 100% sure that a property is unique (because of concurrency issues, just like ActiveRecord). Constraints can also be declared just like indexes separately, see above.

Labels
~~~~~~

The class name maps directly to the label. In the following case both the class name and label are ``Post``

.. code-block:: ruby
class Post
include Neo4j::ActiveNode
end
If you want to specify a different label for your class you can use ``mapped_label_name``:

.. code-block:: ruby
class Post
include Neo4j::ActiveNode
self.mapped_label_name = 'BlogPost'
end
If you would like to use multiple labels you can use class inheritance. In the following case object created with the `Article` model would have both `Post` and `Article` labels. When querying `Article` both labels are required on the nodes as well.

.. code-block:: ruby
class Post
include Neo4j::ActiveNode
end
class Article < Post
end
Serialization
~~~~~~~~~~~~~

Expand Down Expand Up @@ -191,7 +225,10 @@ You can query associations:
comment.post # Post object
comment.post.comments # Original comment and all of it's siblings. Makes just one query
post.comments.authors.posts # All posts of people who have commented on the post. Still makes just one query
You can create associations
You can create associations

.. code-block:: ruby
post.comments = [comment1, comment2] # Removes all existing relationships
post.comments << comment3 # Creates new relationship
Expand Down
56 changes: 30 additions & 26 deletions docs/ActiveRel.rst
@@ -1,9 +1,7 @@
ActiveRel
=========

.. note:: See https://github.com/neo4jrb/neo4j/wiki/Neo4j.rb-v4-Introduction if you are using the master branch from this repo. It contains information about changes to the API.

ActiveRel is Neo4j.rb 3.0's the relationship wrapper. ActiveRel objects share most of their behavior with ActiveNode objects. It is purely optional and offers advanced functionality for complex relationships.
ActiveRel is a module in the ``neo4j`` gem which wraps relationships. ActiveRel objects share most of their behavior with ActiveNode objects. ActiveRel is purely optional and offers advanced functionality for complex relationships.

When to Use?
------------
Expand All @@ -12,19 +10,24 @@ It is not always necessary to use ActiveRel models but if you have the need for

Note that in Neo4j it isn't possible to access relationships except by first accessing a node. Thus `ActiveRel` doesn't implement a `uuid` property like ``ActiveNode``.

Separation of relationship logic instead of shoehorning it into Node models
Validations, callbacks, custom methods, etc.
Centralize relationship type, no longer need to use :type or :origin options in models
... Documentation notes

Separation of relationship logic instead of shoehorning it into Node models

Validations, callbacks, custom methods, etc.

Centralize relationship type, no longer need to use ``:type`` or ``:origin`` options in models

Setup
-----

ActiveRel model definitions have four requirements:

include Neo4j::ActiveRel
call from_class with a valid model constant or :any
call to_class with a valid model constant or :any
call type with a string to define the relationship type Name the file as you would any other model.
* include Neo4j::ActiveRel
* call from_class with a valid model constant or :any
* call to_class with a valid model constant or :any
* call type with a Symbol or String to define the Neo4j relationship type

See the note on from/to at the end of this page for additional information.

.. code-block:: ruby
Expand Down Expand Up @@ -53,8 +56,9 @@ Relationship Creation
---------------------

From an ActiveRel Model
~~~~~~~~~~~~~~~~~~~~~~~

Once setup, ActiveRel models follow the same rules as ActiveNode in regard to properties. Declare them to create setter/getter methods, set them to created_at or updated_at for automatic timestamps.
Once setup, ActiveRel models follow the same rules as ActiveNode in regard to properties. Declare them to create setter/getter methods. You can also set ``created_at`` or ``updated_at`` for automatic timestamps.

ActiveRel instances require related nodes before they can be saved. Set these using the from_node and to_node methods.

Expand All @@ -73,17 +77,15 @@ You can pass these as parameters when calling new or create if you so choose.
rel = EnrolledIn.create(from_node: student, to_node: lesson)
From a `has_many` or `has_one` association
------------------------------------------

Pass the :rel_type option in a declared association with the constant of an ActiveRel model. When that relationship is created, it will add a hidden _classname property with that model's name. The association will use the type declared in the ActiveRel model and it will raise an error if it is included in more than one place.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To take advantage of callbacks and validations, declare your relationship using your ActiveRel model as described above.
Pass the :rel_class option in a declared association with the constant of an ActiveRel model. When that relationship is created, it will add a hidden _classname property with that model's name. The association will use the type declared in the ActiveRel model and it will raise an error if it is included in more than one place.

.. code-block:: ruby
class Student
include Neo4j::ActiveNode
has_many :out, :lessons, rel_class: EnrolledIn
has_many :out, :lessons, rel_class: :EnrolledIn
end
Query and Loading existing relationships
Expand All @@ -98,16 +100,17 @@ Any of these methods can return relationship objects.

.. code-block:: ruby
Student.first.lessons.each_rel{|r| }
Student.first.lessons.each_with_rel{|node, rel| }
Student.first.lessons.each_rel { |r| }
Student.first.lessons.each_with_rel { |node, rel| }
Student.first.query_as(:s).match('s-[rel1:`enrolled_in`]->n2').pluck(:rel1)
These are available as both class or instance methods. Because both each_rel and each_with_rel return enumerables when a block is skipped, you can take advantage of the full suite of enumerable methods:
These are available as both class or instance methods. Because both each_rel and each_with_rel return enumerables when a block is skipped, you can take advantage of the full suite of enumerable methods:

.. code-block:: ruby
Lesson.first.students.each_with_rel.select{|n, r| r.grade > 85}
Lesson.first.students.each_with_rel.select{ |n, r| r.grade > 85 }
Be aware that select would be performed in Ruby after a Cypher query is performed. The example above perform a Cypher query that matches all students with relationships of type enrolled_in to Lesson.first, then it would call select on that.
Be aware that select would be performed in Ruby after a Cypher query is performed. The example above performs a Cypher query that matches all students with relationships of type enrolled_in to Lesson.first, then it would call select on that.

The :where method
~~~~~~~~~~~~~~~~~
Expand All @@ -117,6 +120,7 @@ Because you cannot search for a relationship the way you search for a node, Acti
.. code-block:: ruby
EnrolledIn.where(since: 2002)
# Generates the Cypher:
# "MATCH (node1:`Student`)-[rel1:`enrolled_in`]->(node2:`Lesson`) WHERE rel1.since = 2002 RETURN rel1"
If your from_class is :any, the same query looks like this:
Expand Down Expand Up @@ -155,18 +159,18 @@ Advanced Usage
Separation of Relationship Logic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ActiveRel really shines when you have multiple associations that share a relationship type. You can use a rel model to separate the relationship logic and just let the node models be concerned with the labels of related objects.
ActiveRel really shines when you have multiple associations that share a relationship type. You can use an ActiveRel model to separate the relationship logic and just let the node models be concerned with the labels of related objects.

.. code-block:: ruby
class User
include Neo4j::ActiveNode
property :managed_stats, type: Integer #store the number of managed objects to improve performance
has_many :out, :managed_lessons, model_class: Lesson, rel_class: ManagedRel
has_many :out, :managed_teachers, model_class: Teacher, rel_class: ManagedRel
has_many :out, :managed_events, model_class: Event, rel_class: ManagedRel
has_many :out, :managed_objects, model_class: false, rel_class: ManagedRel
has_many :out, :managed_lessons, model_class: Lesson, rel_class: :ManagedRel
has_many :out, :managed_teachers, model_class: Teacher, rel_class: :ManagedRel
has_many :out, :managed_events, model_class: Event, rel_class: :ManagedRel
has_many :out, :managed_objects, model_class: false, rel_class: :ManagedRel
def update_stats
managed_stats += 1
Expand Down
8 changes: 4 additions & 4 deletions docs/Configuration.rst
Expand Up @@ -50,12 +50,12 @@ Configuration

Of course, even with this option set, you can always override it by calling `model_class: 'ClassName'`.

**log_cypher_queries**
**Default:** ``nil``
**logger**
**Default:** ``nil`` (or ``Rails.logger`` in Rails)

Set to `true` to log queries to STDOUT (done automatically in the Rails console)
A Ruby ``Logger`` object which is used to log Cypher queries (`info` level is used)

**pretty_logged_cypher_queries**
**Default:** ``nil``

When logging cypher queries, should they be formatted with newlines and colors to be more easily readable by humans?
If true, format outputted queries with newlines and colors to be more easily readable by humans

0 comments on commit f0de925

Please sign in to comment.