contentful_model 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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