baby_squeel 0.1.0 → 0.2.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: 4dec1d36d240f113d9122c83549c06601f29124c
4
- data.tar.gz: f97d4bc708832c1895e0e612c6bd76dfe63b6877
3
+ metadata.gz: 3569d1726ce4c979428cbc2fed31fe8103f16405
4
+ data.tar.gz: 7d60acb4736d2a7cdda1e921bcb32ac1b8597601
5
5
  SHA512:
6
- metadata.gz: 7be1b2e3d861f50328cb1a9b05f8e02099a1db3c33dfeda3259a77667291b8f3b373d3e0a395230b1b64af7b8d8c2d559f52e31752c7e20401371e5eaa1fecaf
7
- data.tar.gz: 877ad6134d70a905a9142f92265e059ef48e49edd9ef513445a966b0a2a22758c0e1792be9180b749be5d2673e5e817a15550fe93e465eb61e2440709bd9b770
6
+ metadata.gz: 81b72fb9e8b0f80dc8a0c5e52b215ed89e8f8801407ec2a60747f06171f35b9e6f8d0970c7009573092b17eaaa67ea8fabfa8dcf542b1aa0396b777df966bd4a
7
+ data.tar.gz: fdcbff4355e934d51d6f6e57e19fdbda28a93eac0acb3ea91b4fc1d1d7c9ed84ae06c75dcb500a72325577374df38c0969ebef9dbee45b08a7cd0845de27c374
data/.rubocop.yml CHANGED
@@ -7,9 +7,15 @@ Style/BlockDelimiters:
7
7
  Style/Documentation:
8
8
  Enabled: false
9
9
 
10
+ Style/SpecialGlobalVars:
11
+ Enabled: false
12
+
10
13
  Style/NilComparison:
11
14
  Exclude:
12
15
  - spec/**/*
13
16
 
14
17
  Metrics/LineLength:
15
18
  Max: 100
19
+
20
+ Metrics/AbcSize:
21
+ Enabled: false
data/.travis.yml CHANGED
@@ -16,4 +16,5 @@ env:
16
16
  - AR=4.1.15
17
17
  - AR=4.2.0
18
18
  - AR=4.2.6
19
+ - AR=5.0.0.beta2
19
20
  - AR=5.0.0.beta3
data/README.md CHANGED
@@ -53,41 +53,6 @@ Post.joins(:author).selecting { [id, author.id] }
53
53
  # INNER JOIN "authors" ON "posts"."author_id" = "authors"."id"
54
54
  ```
55
55
 
56
- #### Joins
57
-
58
- ```ruby
59
- Post.joining { author }
60
- # SELECT "posts".* FROM "posts"
61
- # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
62
-
63
- Post.joining { [author.outer, comments] }
64
- # SELECT "posts".* FROM "posts"
65
- # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
66
- # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
67
-
68
- Post.joining { author.comments }
69
- # SELECT "posts".* FROM "posts"
70
- # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
71
- # INNER JOIN "comments" ON "comments"."author_id" = "authors"."id"
72
-
73
- Post.joining { author.outer.comments.outer }
74
- # SELECT "posts".* FROM "posts"
75
- # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
76
- # LEFT OUTER JOIN "comments" ON "comments"."author_id" = "authors"."id"
77
-
78
- Post.joining { author.comments.outer }
79
- # SELECT "posts".* FROM "posts"
80
- # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
81
- # LEFT OUTER JOIN "comments" ON "comments"."author_id" = "authors"."id"
82
-
83
- Post.joining { author.alias('a').on((author.id == author_id) | (author.name == title)) }
84
- # SELECT "posts".* FROM "posts"
85
- # INNER JOIN "authors" "a" ON (
86
- # "authors"."id" = "posts"."author_id" OR
87
- # "authors"."name" = "posts"."title"
88
- # )
89
- ```
90
-
91
56
  #### Wheres
92
57
 
93
58
  ```ruby
@@ -110,6 +75,16 @@ Post.joins(:author).where.has { author.name == 'Ray' }
110
75
  # WHERE "authors"."name" = 'Ray'
111
76
  ```
112
77
 
78
+ Here's the best part. Where conditions will always reference the correct table alias for a given association:
79
+
80
+ ```ruby
81
+ Post.joins(author: :posts).where.has { author.posts.title =~ '%fun%' }
82
+ # SELECT "posts".* FROM "posts"
83
+ # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
84
+ # INNER JOIN "posts" "posts_authors" ON "posts_authors"."author_id" = "authors"."id"
85
+ # WHERE ("posts_authors"."title" LIKE '%fun%')
86
+ ```
87
+
113
88
  #### Orders
114
89
 
115
90
  ```ruby
@@ -130,6 +105,47 @@ Post.joins(:author).ordering { author.id.desc }
130
105
  # ORDER BY "authors"."id" DESC
131
106
  ```
132
107
 
108
+
109
+ #### Joins
110
+
111
+ ```ruby
112
+ Post.joining { author }
113
+ # SELECT "posts".* FROM "posts"
114
+ # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
115
+
116
+ Post.joining { [author.outer, comments] }
117
+ # SELECT "posts".* FROM "posts"
118
+ # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
119
+ # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
120
+
121
+ Post.joining { author.comments }
122
+ # SELECT "posts".* FROM "posts"
123
+ # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
124
+ # INNER JOIN "comments" ON "comments"."author_id" = "authors"."id"
125
+
126
+ Post.joining { author.outer.comments.outer }
127
+ # SELECT "posts".* FROM "posts"
128
+ # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
129
+ # LEFT OUTER JOIN "comments" ON "comments"."author_id" = "authors"."id"
130
+
131
+ Post.joining { author.comments.outer }
132
+ # SELECT "posts".* FROM "posts"
133
+ # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
134
+ # LEFT OUTER JOIN "comments" ON "comments"."author_id" = "authors"."id"
135
+
136
+ Post.joining { author.outer.posts }
137
+ # SELECT "posts".* FROM "posts"
138
+ # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
139
+ # INNER JOIN "posts" "posts_authors" ON "posts_authors"."author_id" = "authors"."id"
140
+
141
+ Post.joining { author.alias('a').on((author.id == author_id) | (author.name == title)) }
142
+ # SELECT "posts".* FROM "posts"
143
+ # INNER JOIN "authors" "a" ON (
144
+ # "authors"."id" = "posts"."author_id" OR
145
+ # "authors"."name" = "posts"."title"
146
+ # )
147
+ ```
148
+
133
149
  #### Functions
134
150
 
135
151
  ```ruby
@@ -158,7 +174,7 @@ You can also run `bin/console` to open up a prompt where you'll have access to s
158
174
 
159
175
  ## Todo
160
176
 
161
- I'd like to support complex joins with explicit outer joins and aliasing.
177
+ + Subqueries
162
178
 
163
179
  ## Contributing
164
180
 
data/Rakefile CHANGED
@@ -15,9 +15,10 @@ def run_version(version, cmd)
15
15
  system({ 'AR' => version }, cmd)
16
16
  end
17
17
 
18
- abort "\nFAILED: #{display}" unless $CHILD_STATUS.success?
18
+ abort "\nFAILED: #{display}" unless $?.success?
19
19
  end
20
20
 
21
+ desc 'Run against all ActiveRecord versions'
21
22
  task 'spec:matrix' do
22
23
  travis = YAML.load_file '.travis.yml'
23
24
 
data/lib/baby_squeel.rb CHANGED
@@ -7,4 +7,5 @@ module BabySqueel
7
7
  end
8
8
 
9
9
  ::ActiveRecord::Base.extend BabySqueel::ActiveRecord::QueryMethods
10
+ ::ActiveRecord::Relation.prepend BabySqueel::ActiveRecord::QueryMethods
10
11
  ::ActiveRecord::QueryMethods::WhereChain.prepend BabySqueel::ActiveRecord::WhereChain
@@ -3,14 +3,17 @@ require 'baby_squeel/dsl'
3
3
  module BabySqueel
4
4
  module ActiveRecord
5
5
  module QueryMethods
6
+ # Constructs Arel for ActiveRecord::Base#joins using the DSL.
6
7
  def joining(&block)
7
- joins DSL.evaluate(self, &block)
8
+ joins DSL.evaluate(unscoped, &block)
8
9
  end
9
10
 
11
+ # Constructs Arel for ActiveRecord::Base#select using the DSL.
10
12
  def selecting(&block)
11
13
  select DSL.evaluate(self, &block)
12
14
  end
13
15
 
16
+ # Constructs Arel for ActiveRecord::Base#order using the DSL.
14
17
  def ordering(&block)
15
18
  order DSL.evaluate(self, &block)
16
19
  end
@@ -18,6 +21,7 @@ module BabySqueel
18
21
 
19
22
  module WhereChain
20
23
  if ::ActiveRecord::VERSION::MAJOR > 4
24
+ # Constructs Arel for ActiveRecord::Base#where using the DSL.
21
25
  def has(&block)
22
26
  opts = DSL.evaluate(@scope, &block)
23
27
  factory = @scope.send(:where_clause_factory)
@@ -25,6 +29,7 @@ module BabySqueel
25
29
  @scope
26
30
  end
27
31
  else
32
+ # Constructs Arel for ActiveRecord::Base#where using the DSL.
28
33
  def has(&block)
29
34
  @scope.where_values += [DSL.evaluate(@scope, &block)]
30
35
  @scope
@@ -1,42 +1,45 @@
1
1
  require 'baby_squeel/table'
2
- require 'baby_squeel/join_dependency'
3
2
 
4
3
  module BabySqueel
5
- class AliasingError < StandardError
6
- MESSAGE =
7
- 'Attempted to alias \'%{association}\' as \'%{alias_name}\', but the ' \
8
- 'association was implicitly joined. Either join the association ' \
9
- 'with `on` or remove the alias.'.freeze
10
-
11
- def initialize(association, alias_name)
12
- super format(MESSAGE, association: association, alias_name: alias_name)
13
- end
14
- end
15
-
16
4
  class Association < Table
17
- def initialize(parent, reflection)
18
- @parent = parent
19
- @reflection = reflection
20
- super(@reflection.klass)
21
- end
22
-
23
- def _arel
24
- props[:on] ? super : ([*@parent._arel] + join_constraints)
5
+ class AliasingError < StandardError
6
+ MESSAGE =
7
+ 'Attempted to alias \'%{association}\' as \'%{alias_name}\', but the ' \
8
+ 'association was implicitly joined. Either join the association ' \
9
+ 'with `on` or remove the alias.'.freeze
10
+
11
+ def initialize(association, alias_name)
12
+ super format(MESSAGE, association: association, alias_name: alias_name)
13
+ end
25
14
  end
26
15
 
27
- private
28
-
29
- def join_constraints
30
- if props[:table].is_a? Arel::Nodes::TableAlias
31
- raise AliasingError.new(@reflection.name, props[:table].right)
32
- end
16
+ attr_reader :_reflection
33
17
 
34
- JoinDependency.new(@scope, @reflection, props[:join]).constraints
18
+ def initialize(parent, reflection)
19
+ @parent = parent
20
+ @_reflection = reflection
21
+ super(@_reflection.klass)
35
22
  end
36
23
 
37
- def spawn
38
- Association.new(@parent, @reflection).tap do |table|
39
- table.props = props
24
+ # Intelligently constructs Arel nodes. There are three outcomes:
25
+ #
26
+ # 1. The user explicitly constructed their join using #on.
27
+ # See BabySqueel::Table#_arel.
28
+ #
29
+ # 2. The user aliased an implicitly joined association. ActiveRecord's
30
+ # join dependency gives us no way of handling this, so we have to
31
+ # throw an error.
32
+ #
33
+ # 3. The user implicitly joined this association, so we pass this
34
+ # association up the tree until it hits the top-level BabySqueel::Table.
35
+ # Once it gets there, Arel join nodes will be constructed.
36
+ def _arel(associations = [])
37
+ if _on
38
+ super
39
+ elsif _table.is_a? Arel::Nodes::TableAlias
40
+ raise AliasingError.new(_reflection.name, _table.right)
41
+ else
42
+ @parent._arel([self, *associations])
40
43
  end
41
44
  end
42
45
  end
@@ -4,14 +4,28 @@ require 'baby_squeel/association'
4
4
 
5
5
  module BabySqueel
6
6
  class DSL < Table
7
+ # Evaluates a block in the context of a new DSL instance.
7
8
  def self.evaluate(scope, &block)
8
9
  new(scope).evaluate(&block)
9
10
  end
10
11
 
12
+ # Create a SQL function. See Arel::Nodes::NamedFunction.
13
+ #
14
+ # ==== Arguments
15
+ #
16
+ # * +name+ - The name of a SQL function (ex. coalesce).
17
+ # * +args+ - The arguments to be passed to the SQL function.
18
+ #
19
+ # ==== Example
20
+ # Post.select { func('coalesce', id, 1) }
21
+ # #=> SELECT COALESCE("posts"."id", 1) FROM "posts"
22
+ #
11
23
  def func(name, *args)
12
24
  Nodes.wrap Arel::Nodes::NamedFunction.new(name.to_s, args)
13
25
  end
14
26
 
27
+ # Evaluates a DSL block. If arity is given, this method
28
+ # `yield` itself, rather than `instance_eval`.
15
29
  def evaluate(&block)
16
30
  if block.arity.zero?
17
31
  Nodes.unwrap instance_eval(&block)
@@ -1,55 +1,39 @@
1
- # Props to Arel Helpers
2
- # https://github.com/camertron/arel-helpers/blob/master/lib/arel-helpers/join_association.rb
3
- #
4
- # This is really, really ugly.
5
- #
6
1
  module BabySqueel
7
2
  class JoinDependency
8
- def initialize(scope, reflection, join_type)
3
+ def initialize(scope, associations = [])
9
4
  @scope = scope
10
- @reflection = reflection
11
- @join_type = join_type
5
+ @associations = associations
12
6
  end
13
7
 
14
- if ::ActiveRecord::VERSION::MAJOR > 4
15
- def constraints
16
- dependency.join_constraints([], @join_type).flat_map(&:joins)
17
- end
18
- elsif ::ActiveRecord::VERSION::STRING >= '4.2.0'
19
- def constraints
20
- dependency.join_constraints([]).flat_map do |constraint|
21
- constraint.joins.map { |join| rebuild join }
22
- end
23
- end
24
- elsif ::ActiveRecord::VERSION::STRING >= '4.1.0'
25
- def constraints
26
- dependency.join_constraints([]).flat_map { |join| rebuild join }
27
- end
28
- else
29
- def constraints
30
- manager = Arel::SelectManager.new(@scope)
31
-
32
- dependency.join_associations.each do |assoc|
33
- assoc.join_type = @join_type
34
- assoc.join_to(manager)
35
- end
36
-
37
- manager.join_sources
8
+ # Converts an array of BabySqueel::Associations into an array
9
+ # of Arel join nodes.
10
+ #
11
+ # Each association is built individually so that the correct
12
+ # Arel join node will be used for each individual association.
13
+ def constraints
14
+ @associations.each.with_index.inject([]) do |joins, (assoc, i)|
15
+ inject @associations[0..i], joins, assoc._join
38
16
  end
39
17
  end
40
18
 
41
19
  private
42
20
 
43
- def rebuild(join)
44
- @join_type.new(join.left, join.right)
21
+ def inject(associations, theirs, join_node)
22
+ names = join_names associations
23
+ mine = build names, join_node
24
+ theirs + mine[theirs.length..-1]
45
25
  end
46
26
 
47
- def dependency
48
- ::ActiveRecord::Associations::JoinDependency.new(
49
- @reflection.active_record,
50
- [@reflection.name],
51
- []
52
- )
27
+ def build(names, join_node)
28
+ @scope.joins(names).join_sources.map do |join|
29
+ join_node.new(join.left, join.right)
30
+ end
31
+ end
32
+
33
+ def join_names(associations = [])
34
+ associations.reverse.inject({}) do |names, assoc|
35
+ { assoc._reflection.name => names }
36
+ end
53
37
  end
54
38
  end
55
39
  end
@@ -3,6 +3,8 @@ require 'baby_squeel/operators'
3
3
  module BabySqueel
4
4
  module Nodes
5
5
  class << self
6
+ # Wraps an Arel node in a Proxy so that it can
7
+ # be extended.
6
8
  def wrap(arel)
7
9
  if arel.class.parents.include?(Arel)
8
10
  Generic.new(arel)
@@ -11,6 +13,8 @@ module BabySqueel
11
13
  end
12
14
  end
13
15
 
16
+ # Unwraps a BabySqueel::Proxy before being passed to
17
+ # ActiveRecord.
14
18
  def unwrap(node)
15
19
  if node.respond_to? :_arel
16
20
  node._arel
@@ -22,9 +26,8 @@ module BabySqueel
22
26
  end
23
27
  end
24
28
 
25
- # This proxy class allows us to quack like
26
- # any arel object. When a method missing is
27
- # hit, we'll instantiate a new proxy object.
29
+ # This proxy class allows us to quack like any arel object. When a
30
+ # method missing is hit, we'll instantiate a new proxy object.
28
31
  class Proxy < ActiveSupport::ProxyObject
29
32
  # Resolve constants the normal way
30
33
  def self.const_missing(name)
@@ -52,11 +55,11 @@ module BabySqueel
52
55
  end
53
56
  end
54
57
 
55
- # This is a generic proxy class that includes
56
- # all possible modules. In the future, these
57
- # proxy classes should be more specific and
58
- # only include necessary/applicable modules.
58
+ # This is a generic proxy class that includes all possible modules.
59
+ # In the future, these proxy classes should be more specific and only
60
+ # include necessary/applicable modules.
59
61
  class Generic < Proxy
62
+ extend Operators::ArelAliasing
60
63
  include Arel::OrderPredications
61
64
  include Operators::Comparison
62
65
  include Operators::Equality
@@ -64,5 +67,23 @@ module BabySqueel
64
67
  include Operators::Grouping
65
68
  include Operators::Matching
66
69
  end
70
+
71
+ class Attribute < Generic
72
+ def initialize(parent, name)
73
+ @parent = parent
74
+ @name = name
75
+ super(parent._table[name])
76
+ end
77
+
78
+ def _arel
79
+ parent_arel = @parent._arel
80
+
81
+ if parent_arel && parent_arel.last
82
+ parent_arel.last.left[@name]
83
+ else
84
+ super
85
+ end
86
+ end
87
+ end
67
88
  end
68
89
  end
@@ -1,6 +1,17 @@
1
1
  module BabySqueel
2
2
  module Operators
3
3
  module ArelAliasing
4
+ # Allows the creation of shorthands for Arel methods.
5
+ #
6
+ # ==== Arguments
7
+ #
8
+ # * +operator+ - A custom operator.
9
+ # * +arel_name+ - The name of the Arel method you want to alias.
10
+ #
11
+ # ==== Example
12
+ # BabySqueel::Nodes::Generic.arel_alias :unlike, :does_not_match
13
+ # Post.where { title.unlike 'something' }
14
+ #
4
15
  def arel_alias(operator, arel_name)
5
16
  define_method operator do |other|
6
17
  send(arel_name, other)
@@ -23,6 +34,17 @@ module BabySqueel
23
34
  end
24
35
 
25
36
  module Generic
37
+ # Create a SQL operation. See Arel::Nodes::InfixOperation.
38
+ #
39
+ # ==== Arguments
40
+ #
41
+ # * +operator+ - A SQL operator.
42
+ # * +other+ - The argument to be passed to the SQL operator.
43
+ #
44
+ # ==== Example
45
+ # Post.select { title.op('||', 'diddly') }
46
+ # #=> SELECT "posts"."title" || 'diddly' FROM "posts"
47
+ #
26
48
  def op(operator, other)
27
49
  Nodes.wrap Arel::Nodes::InfixOperation.new(operator, self, other)
28
50
  end
@@ -1,3 +1,5 @@
1
+ require 'baby_squeel/join_dependency'
2
+
1
3
  module BabySqueel
2
4
  class AssociationNotFoundError < StandardError
3
5
  def initialize(scope, name)
@@ -6,16 +8,21 @@ module BabySqueel
6
8
  end
7
9
 
8
10
  class Table
9
- attr_writer :props
11
+ attr_accessor :_on, :_join, :_table
10
12
 
11
13
  def initialize(scope)
12
14
  @scope = scope
15
+ @_table = scope.arel_table
16
+ @_join = Arel::Nodes::InnerJoin
13
17
  end
14
18
 
19
+ # See Arel::Table#[]
15
20
  def [](key)
16
- Nodes.wrap props[:table][key]
21
+ Nodes::Attribute.new(self, key)
17
22
  end
18
23
 
24
+ # Constructs a new BabySqueel::Association. Raises
25
+ # an exception if the association is not found.
19
26
  def association(name)
20
27
  if reflection = @scope.reflect_on_association(name)
21
28
  Association.new(self, reflection)
@@ -24,58 +31,63 @@ module BabySqueel
24
31
  end
25
32
  end
26
33
 
34
+ # Alias a table. This is only possible when joining
35
+ # an association explicitly.
27
36
  def alias(alias_name)
28
- spawn.alias! alias_name
37
+ clone.alias! alias_name
29
38
  end
30
39
 
31
40
  def alias!(alias_name)
32
- props.store :table, props[:table].alias(alias_name)
41
+ self._table = _table.alias(alias_name)
33
42
  self
34
43
  end
35
44
 
45
+ # Instruct the table to be joined with an INNER JOIN.
36
46
  def outer
37
- spawn.outer!
47
+ clone.outer!
38
48
  end
39
49
 
40
50
  def outer!
41
- props.store :join, Arel::Nodes::OuterJoin
51
+ self._join = Arel::Nodes::OuterJoin
42
52
  self
43
53
  end
44
54
 
55
+ # Instruct the table to be joined with an INNER JOIN.
45
56
  def inner
46
- spawn.inner!
57
+ clone.inner!
47
58
  end
48
59
 
49
60
  def inner!
50
- props.store :join, Arel::Nodes::InnerJoin
61
+ self._join = Arel::Nodes::InnerJoin
51
62
  self
52
63
  end
53
64
 
65
+ # Specify an explicit join.
54
66
  def on(node)
55
- spawn.on! node
67
+ clone.on! node
56
68
  end
57
69
 
58
70
  def on!(node)
59
- props.store :on, Arel::Nodes::On.new(node)
71
+ self._on = Arel::Nodes::On.new(node)
60
72
  self
61
73
  end
62
74
 
63
- def _arel
64
- props[:join].new(props[:table], props[:on]) if props[:on]
75
+ # This method will be invoked by BabySqueel::Nodes::unwrap. When called,
76
+ # there are two possible outcomes:
77
+ #
78
+ # 1. Join explicitly using an on clause.
79
+ # 2. Resolve the assocition's join clauses using ActiveRecord.
80
+ #
81
+ def _arel(associations = [])
82
+ if _on
83
+ _join.new(_table, _on)
84
+ else
85
+ JoinDependency.new(@scope, associations).constraints
86
+ end
65
87
  end
66
88
 
67
89
  private
68
90
 
69
- def props
70
- @props ||= { table: @scope.arel_table, join: Arel::Nodes::InnerJoin }
71
- end
72
-
73
- def spawn
74
- Table.new(@scope).tap do |table|
75
- table.props = props
76
- end
77
- end
78
-
79
91
  def resolve(name)
80
92
  if @scope.column_names.include?(name.to_s)
81
93
  self[name]
@@ -1,3 +1,3 @@
1
1
  module BabySqueel
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: baby_squeel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ray Zane
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-03-16 00:00:00.000000000 Z
11
+ date: 2016-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord