contentful_model 0.0.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b0c557075e720d6f54dd624f9342c0b9a001909e
4
- data.tar.gz: 8c0705dac36560cf57a4c09022e6c0f9ee77c0db
3
+ metadata.gz: c5c1cf9bc5fe07bc623e3d1bc3d3ae0b8d85c9fb
4
+ data.tar.gz: 918055df94e083db1816f5fcc527e54e831a2227
5
5
  SHA512:
6
- metadata.gz: 8b9463610d3aa57d4abde50284b46a9293b70772571aff50d9c3d6b6698087d2ebe606148ffd6067082213e458fd59210761fd9d406e97f8622c9934970dad70
7
- data.tar.gz: 8e9ed3329c34045efd4211dae3b5526b1c3abdc64a9c8d35b220e5dc2194f972674e84c9df100d4ea476da29b33cd9a987e07a29e84b8ef74b6ca3850ae34b34
6
+ metadata.gz: 0a754f0bb73a6909c161f66a24c498d6c4287f50bc0581fe90688973dada28402dab54cf60d2b92bc29e0a9c1fbdf7c6f77d48cb23e78dedd53738fec184205d
7
+ data.tar.gz: 2423688b78f546b97b64b318772ebe7de72d773bbe7cc0c7da065d3eec039616887525c5ddd7a160d9970ea48540a03aafe2f2a8069dbc8e21abe9d2246ba23a
@@ -1,9 +1,7 @@
1
+ require 'require_all'
1
2
  require 'contentful'
2
- require "contentful_model/query"
3
- require "contentful_model/queries"
4
- require "contentful_model/chainable_queries"
5
- require "contentful_model/associations"
6
- require "contentful_model/base"
3
+ require_rel '.'
4
+
7
5
  require "active_support/all"
8
6
 
9
7
  module ContentfulModel
@@ -0,0 +1,13 @@
1
+ # A module to map relationships, a little like ActiveRecord::Relation
2
+ # This is necessary because Contentful::Link classes are not 2-way, so you can't
3
+ # get the parent from a child.
4
+ module ContentfulModel
5
+ module Associations
6
+ def self.included(base)
7
+ base.include HasMany
8
+ base.include HasOne
9
+ base.include BelongsTo
10
+ base.include BelongsToMany
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ module ContentfulModel
2
+ module Associations
3
+ module BelongsTo
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ # belongs_to is called on the child, and creates methods for mapping to the parent
10
+ # @param association_name [Symbol] the singular name of the parent
11
+ def belongs_to(association_name, opts = {})
12
+ raise NotImplementedError, "Contentful doesn't have a singular belongs_to relationship. Use belongs_to_many instead."
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ module ContentfulModel
2
+ module Associations
3
+ module BelongsToMany
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+ module ClassMethods
8
+ # belongs_to_many implements a has_many association from the opposite end, and allows a call to the association name
9
+ # to return all instances for which this object is a child.
10
+ #
11
+ # It's moderately expensive because we have to iterate over every parent, checking whether this instance
12
+ # is in the children. It requires one API call.
13
+
14
+ # class Bar
15
+ # belongs_to_many :foos, class_name: Foo, inverse_of: :special_bars
16
+ # end
17
+
18
+ # In this example, children on the parent are accessed through an association called special_bars.
19
+
20
+ # @param association_names [Symbol] plural name of the class we need to search through, to find this class
21
+ # @param options [true, Hash] options
22
+ def belongs_to_many(association_names, opts = {})
23
+ default_options = {
24
+ class_name: association_names.to_s.singularize.classify,
25
+ inverse_of: self.to_s.underscore.to_sym
26
+ }
27
+ options = default_options.merge(opts)
28
+ if self.respond_to?(association_names)
29
+ self.send(association_names)
30
+ else
31
+ define_method "#{association_names}" do
32
+ parents = instance_variable_get(:"@#{association_names}")
33
+ if parents.nil?
34
+ #get the parent class objects as an array
35
+ parent_objects = options[:class_name].constantize.send(:all).send(:load)
36
+ #iterate through parent objects and see if any of the children include the same ID as the method
37
+ parents = parent_objects.select do |parent_object|
38
+ #check to see if the parent object responds to the plural or singular.
39
+ if parent_object.respond_to?(:"#{options[:inverse_of].to_s.pluralize}")
40
+ collection_of_children_on_parent = parent_object.send(:"#{options[:inverse_of].to_s.pluralize}")
41
+ #get the collection of children from the parent. This *might* be nil if the parent doesn't have
42
+ # any children, in which case, just skip over this parent item and move on to the next.
43
+ if collection_of_children_on_parent.nil?
44
+ next
45
+ else
46
+ collection_of_children_on_parent.collect(&:id).include?(id)
47
+ end
48
+ else
49
+ #if it doesn't respond to the plural, assume singular
50
+ child_on_parent = parent_object.send(:"#{options[:inverse_of]}")
51
+ # Do the same skipping routine on nil.
52
+ if child_on_parent.nil?
53
+ next
54
+ else
55
+ child_on_parent.send(:id) == id
56
+ end
57
+
58
+ end
59
+ end
60
+ instance_variable_set(:"@#{association_names}",parents)
61
+ end
62
+ parents
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,44 @@
1
+ module ContentfulModel
2
+ module Associations
3
+ module HasMany
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ # has_many is called on the parent model
10
+ #
11
+ # e.g
12
+ # class Foo
13
+ # has_many :bars, class_name: "Something"
14
+ # end
15
+ # The only reason for this method is that we might want to specify a relationship which is different
16
+ # from the name of the model we're calling. If you specify a class_name, the method called on the parent will
17
+ # be that. e.g. .somethings in this example
18
+ # @param association_names [Symbol] the name of the child model, as a plural symbol
19
+ def has_many(association_names, options = {})
20
+ default_options = {
21
+ class_name: association_names.to_s.singularize.classify
22
+ }
23
+ options = default_options.merge(options)
24
+ define_method association_names do
25
+ begin
26
+ # try calling the association name directly on the superclass - will be picked up by
27
+ # ContentfulModel::Base#method_missing and return a value if there is one for the attribute
28
+ super()
29
+ rescue ContentfulModel::AttributeNotFoundError
30
+ # If AttributeNotFoundError is raised, that means that the association name isn't available on the object.
31
+ # We try to call the class name (pluralize) instead, or give up and return an empty collection
32
+ if options[:class_name].pluralize.underscore.to_sym != association_names
33
+ self.send(options[:class_name].pluralize.underscore.to_sym)
34
+ else
35
+ #return an empty collection if the class name was the same as the association name and there's no attribute on the object.
36
+ []
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ module ContentfulModel
2
+ module Associations
3
+ module HasOne
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ #has_one defines a method on the parent which wraps around the superclass's implementation. In most cases this
10
+ #will end up at ContentfulModel::Base#method_missing and look at the fields on a content object.
11
+ #We wrap around it like this so we can specify a class_name option to call a different method from the association
12
+ #name.
13
+ # class Foo
14
+ # has_one :special_bar, class_name: "Bar"
15
+ # end
16
+ # @param association_name [Symbol] the name of the association. In this case Foo.special_bar.
17
+ # @param options [Hash] a hash, the only key of which is important is class_name.
18
+ def has_one(association_name, options = {})
19
+ default_options = {
20
+ class_name: association_name.to_s.classify
21
+ }
22
+ options = default_options.merge(options)
23
+ # Define a method which matches the association name
24
+ define_method association_name do
25
+ begin
26
+ # Start by calling the association name as a method on the superclass. This will probably end up at ContentfulModel::Base#method_missing
27
+ super()
28
+ rescue ContentfulModel::AttributeNotFoundError
29
+ # If method_missing returns an error, the field doesn't exist. If a class is specified, try that.
30
+ if options[:class_name].underscore.to_sym != association_name
31
+ self.send(options[:class_name].underscore.to_sym)
32
+ else
33
+ #otherwise give up and return nil
34
+ nil
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -12,12 +12,22 @@ module ContentfulModel
12
12
  def method_missing(method, *args, &block)
13
13
  result = fields[:"#{method.to_s.camelize(:lower)}"]
14
14
  if result.nil?
15
- super
15
+ raise ContentfulModel::AttributeNotFoundError, "no attribute #{method} found"
16
16
  else
17
+ # if there's no coercion specified, return the result
17
18
  if self.class.coercions[method].nil?
18
19
  return result
19
- else
20
+
21
+ #if there's a coercion specified for the field and it's a proc, pass the result
22
+ #to the proc
23
+ elsif self.class.coercions[method].is_a?(Proc)
24
+ return self.class.coercions[method].call(result)
25
+ #provided the coercion is in the COERCIONS constant, call the proc on that
26
+ elsif !self.class::COERCIONS[self.class.coercions[method]].nil?
20
27
  return self.class::COERCIONS[self.class.coercions[method]].call(result)
28
+ else
29
+ #... or just return the result
30
+ return result
21
31
  end
22
32
  end
23
33
  end
@@ -67,8 +77,12 @@ module ContentfulModel
67
77
  client.content_type(@content_type_id)
68
78
  end
69
79
 
70
- def coerce_field(coercions_hash)
71
- self.coercions = coercions_hash
80
+ def coerce_field(*coercions)
81
+ @coercions ||= {}
82
+ coercions.each do |coercions_hash|
83
+ @coercions.merge!(coercions_hash)
84
+ end
85
+ @coercions
72
86
  end
73
87
 
74
88
  end
@@ -0,0 +1,4 @@
1
+ module ContentfulModel
2
+ class AssociationError < StandardError; end
3
+ class AttributeNotFoundError < StandardError; end
4
+ end
@@ -1,3 +1,3 @@
1
1
  module ContentfulModel
2
- VERSION = "0.0.8"
2
+ VERSION = "0.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contentful_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Error Creative Studio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-24 00:00:00.000000000 Z
11
+ date: 2015-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: contentful
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: require_all
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description: A wrapper around the Contentful gem to give you a base class to inherit
56
70
  your models from
57
71
  email:
@@ -63,9 +77,14 @@ files:
63
77
  - MIT-LICENSE
64
78
  - Rakefile
65
79
  - lib/contentful_model.rb
66
- - lib/contentful_model/associations.rb
80
+ - lib/contentful_model/associations/associations.rb
81
+ - lib/contentful_model/associations/belongs_to.rb
82
+ - lib/contentful_model/associations/belongs_to_many.rb
83
+ - lib/contentful_model/associations/has_many.rb
84
+ - lib/contentful_model/associations/has_one.rb
67
85
  - lib/contentful_model/base.rb
68
86
  - lib/contentful_model/chainable_queries.rb
87
+ - lib/contentful_model/errors.rb
69
88
  - lib/contentful_model/queries.rb
70
89
  - lib/contentful_model/query.rb
71
90
  - lib/contentful_model/version.rb
@@ -1,61 +0,0 @@
1
- # A module to map relationships, a little like ActiveRecord::Relation
2
- # This is necessary because Contentful::Link classes are not 2-way, so you can't
3
- # get the parent from a child.
4
- module ContentfulModel
5
- module Associations
6
- def self.included(base)
7
- base.extend ClassMethods
8
- end
9
-
10
- module ClassMethods
11
- # has_many is called on the parent model, and sets an instance var on the child
12
- # which is named the plural of the class this module is mixed into.
13
- #
14
- # e.g
15
- # class Foo
16
- # has_many :bars
17
- # end
18
- # @param classname [Symbol] the name of the child model, as a plural symbol
19
- def has_many(classname)
20
- #define an instance method called the same as the arg passed in
21
- #e.g. bars()
22
- define_method "#{classname}" do
23
- # call bars() on super, and for each, call bar=(self)
24
- super().collect do |instance|
25
- instance.send(:"#{self.class.to_s.singularize.camelize(:lower)}=",self)
26
- #return the instance to the collect() method
27
- instance
28
- end
29
- end
30
- end
31
-
32
- # has_one is called on the parent model, and sets a single instance var on the child
33
- # it's conceptually identical to `has_many()`
34
- # which is named the singular of the class this module is mixed into
35
- def has_one(classname)
36
- define_method "#{classname}" do
37
- super().send(:"#{self.class.to_s.singularize.camelize(:lower)}=",self)
38
- instance
39
- end
40
- end
41
-
42
-
43
- # belongs_to is called on the child, and creates methods for mapping to the parent
44
- # @param classname [Symbol] the singular name of the parent
45
- def belongs_to(classname)
46
- raise ArgumentError, "belongs_to requires a class name as a symbol" unless classname.is_a?(Symbol)
47
- define_method "#{classname}" do
48
- #this is where we need to return the parent class
49
- self.instance_variable_get(:"@#{classname}")
50
- end
51
-
52
- define_method "#{classname}=" do |instance|
53
- #this is where we need to set the class name
54
- self.instance_variable_set(:"@#{classname}",instance)
55
- end
56
-
57
-
58
- end
59
- end
60
- end
61
- end