find_or_create_on_scopes 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,26 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+ .bundle
21
+ .rvmrc
22
+ test.sqlite
23
+
24
+ ## PROJECT::DOCUMENTATION
25
+ .yardoc
26
+ doc
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ -cfs
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source :rubygems
2
+
3
+ # DEPENDENCIES
4
+ gem 'activerecord', require: 'active_record'
5
+
6
+ # DEVELOPMENT
7
+ gem 'jeweler'
8
+ gem 'yard'
9
+ gem 'RedCloth', require: 'redcloth'
10
+ gem 'sqlite3'
11
+
12
+ # TEST
13
+ gem 'rspec'
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ RedCloth (4.2.3)
5
+ activemodel (3.0.1)
6
+ activesupport (= 3.0.1)
7
+ builder (~> 2.1.2)
8
+ i18n (~> 0.4.1)
9
+ activerecord (3.0.1)
10
+ activemodel (= 3.0.1)
11
+ activesupport (= 3.0.1)
12
+ arel (~> 1.0.0)
13
+ tzinfo (~> 0.3.23)
14
+ activesupport (3.0.1)
15
+ arel (1.0.1)
16
+ activesupport (~> 3.0.0)
17
+ builder (2.1.2)
18
+ diff-lcs (1.1.2)
19
+ ffi (0.6.3)
20
+ rake (>= 0.8.7)
21
+ gemcutter (0.6.1)
22
+ git (1.2.5)
23
+ i18n (0.4.2)
24
+ jeweler (1.4.0)
25
+ gemcutter (>= 0.1.0)
26
+ git (>= 1.2.5)
27
+ rubyforge (>= 2.0.0)
28
+ json_pure (1.4.6)
29
+ rake (0.8.7)
30
+ rspec (2.0.1)
31
+ rspec-core (~> 2.0.1)
32
+ rspec-expectations (~> 2.0.1)
33
+ rspec-mocks (~> 2.0.1)
34
+ rspec-core (2.0.1)
35
+ rspec-expectations (2.0.1)
36
+ diff-lcs (>= 1.1.2)
37
+ rspec-mocks (2.0.1)
38
+ rspec-core (~> 2.0.1)
39
+ rspec-expectations (~> 2.0.1)
40
+ rubyforge (2.0.4)
41
+ json_pure (>= 1.1.7)
42
+ sqlite3 (0.1.1)
43
+ ffi (>= 0.6.3)
44
+ tzinfo (0.3.23)
45
+ yard (0.6.1)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ RedCloth
52
+ activerecord
53
+ jeweler
54
+ rspec
55
+ sqlite3
56
+ yard
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Tim Morgan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,34 @@
1
+ h1. find_or_create_on_scopes -- Conditional creators for ActiveRecord
2
+
3
+ | *Author* | Tim Morgan |
4
+ | *Version* | 1.0 (Oct 29, 2010) |
5
+ | *License* | Released under the MIT license. |
6
+
7
+ h2. About
8
+
9
+ @find_or_create_on_scopes@ adds handy @find_or_create@-type methods to @ActiveRecord@. You can use these methods to conditionally create, initialize, or update records depending on the presence of similar records. They work on your base model class and any scopes derived therefrom. (See _Usage_ for examples.)
10
+
11
+ h2. Installation
12
+
13
+ *Important Note:* This gem requires Ruby 1.9. Ruby 1.8 is not and will never be supported.
14
+
15
+ To install, simply add the @find_or_create_on_scopes@ gem to your Rails application's @Gemfile@:
16
+
17
+ <pre><code>
18
+ gem 'find_or_create_on_scopes'
19
+ </code></pre>
20
+
21
+ h2. Usage
22
+
23
+ See the @FindOrCreateOnScopes@ module documentation for the complete list of conditional creators/updaters. Some basic examples to give you an overview:
24
+
25
+ <pre><code>
26
+ # Tries to find a user named 'riscfuture'. If such a User exists, returns it. If
27
+ # not, creates a user and sets its password to '123'.
28
+ User.where(login: 'riscfuture').find_or_create(password: '123')
29
+
30
+ # Tries to find a user named 'riscfuture'. If such a User exists, updates its
31
+ # password to '123'. If not, creates a new user and sets its password to '123'.
32
+ # Raises an exception if validation fails.
33
+ User.where(login: 'riscfuture').create_or_update!(password: '123')
34
+ </code></pre>
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ require 'rake'
2
+ begin
3
+ require 'bundler'
4
+ rescue LoadError
5
+ puts "Bundler is not installed; install with `gem install bundler`."
6
+ exit 1
7
+ end
8
+
9
+ Bundler.require :default
10
+
11
+ Jeweler::Tasks.new do |gem|
12
+ gem.name = "find_or_create_on_scopes"
13
+ gem.summary = %Q{find_or_create-type methods on ActiveRecord scopes}
14
+ gem.description = %Q{Adds methods to ActiveRecord for conditionally finding, creating, or updating records.}
15
+ gem.email = "git@timothymorgan.info"
16
+ gem.homepage = "http://github.com/riscfuture/find_or_create_on_scopes"
17
+ gem.authors = [ "Tim Morgan" ]
18
+ gem.required_ruby_version = '>= 1.9'
19
+ gem.add_dependency "activerecord", ">= 0"
20
+ end
21
+ Jeweler::GemcutterTasks.new
22
+
23
+ require 'rspec/core/rake_task'
24
+ RSpec::Core::RakeTask.new
25
+
26
+ YARD::Rake::YardocTask.new('doc') do |doc|
27
+ doc.options << "-m" << "textile"
28
+ doc.options << "--protected"
29
+ doc.options << "-r" << "README.textile"
30
+ doc.options << "-o" << "doc"
31
+ doc.options << "--title" << "find_or_create_on_scopes Documentation".inspect
32
+
33
+ doc.files = [ 'lib/**/*', 'README.textile' ]
34
+ end
35
+
36
+ task(default: :spec)
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{find_or_create_on_scopes}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Tim Morgan"]
12
+ s.date = %q{2010-10-29}
13
+ s.description = %q{Adds methods to ActiveRecord for conditionally finding, creating, or updating records.}
14
+ s.email = %q{git@timothymorgan.info}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.textile"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ ".rspec",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE",
26
+ "README.textile",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "find_or_create_on_scopes.gemspec",
30
+ "lib/find_or_create_on_scopes.rb",
31
+ "spec/find_or_create_on_scopes_spec.rb",
32
+ "spec/spec_helper.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/riscfuture/find_or_create_on_scopes}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.required_ruby_version = Gem::Requirement.new(">= 1.9")
38
+ s.rubygems_version = %q{1.3.7}
39
+ s.summary = %q{find_or_create-type methods on ActiveRecord scopes}
40
+ s.test_files = [
41
+ "spec/find_or_create_on_scopes_spec.rb",
42
+ "spec/spec_helper.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<activerecord>, [">= 0"])
51
+ else
52
+ s.add_dependency(%q<activerecord>, [">= 0"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<activerecord>, [">= 0"])
56
+ end
57
+ end
58
+
@@ -0,0 +1,86 @@
1
+ # Adds @find_or_create@-type methods to named scopes.
2
+
3
+ module FindOrCreateOnScopes
4
+
5
+ # Locates a record according to the current scope. Returns the record if
6
+ # found. If not found, creates a new record with the options of the current
7
+ # scope and @create_options@.
8
+ #
9
+ # @param [Hash] create_options Attributes to apply to the record if it's newly
10
+ # created.
11
+ # @yield [record] Yields the record before it is saved.
12
+ # @yieldparam [ActiveRecord::Base] The found or created record before it is
13
+ # saved.
14
+ # @return [ActiveRecord::Base] The found or created record.
15
+
16
+ def find_or_create(create_options={}, &block)
17
+ find_or_initialize_and_do :save, create_options, &block
18
+ end
19
+
20
+ # Same as {#find_or_create} but calls @save!@ instead of @save@ on the record.
21
+ #
22
+ # @see #find_or_create
23
+
24
+ def find_or_create!(create_options={}, &block)
25
+ find_or_initialize_and_do :save!, create_options, &block
26
+ end
27
+
28
+ # Same as {#find_or_create} but does not save the record. Please note that
29
+ # unless this method is called in a transaction, you might have a race
30
+ # condition when trying to save the record.
31
+ #
32
+ # @see #find_or_create
33
+
34
+ def find_or_initialize(init_options={}, &block)
35
+ find_or_initialize_and_do nil, init_options, &block
36
+ end
37
+
38
+ # Locates a record according to the current scope. Updates the record with
39
+ # @update_options@ if found. If not found, creates a new record with the
40
+ # current scope's options and @update_options@.
41
+ #
42
+ # @param [Hash] update_options Attributes to apply to the record whether or
43
+ # not it's newly created.
44
+ # @yield [record] Yields the record before it is saved.
45
+ # @yieldparam [ActiveRecord::Base] The found or created record before it is
46
+ # saved.
47
+ # @return [ActiveRecord::Base] The found or created record.
48
+
49
+ def create_or_update(update_options={}, &block)
50
+ create_or_update_and_do :save, update_options, &block
51
+ end
52
+
53
+ # Same as {#create_or_update} but calls @save!@ instead of @save@ on the
54
+ # record.
55
+ #
56
+ # @see #create_or_update
57
+
58
+ def create_or_update!(update_options={}, &block)
59
+ create_or_update_and_do :save!, update_options, &block
60
+ end
61
+
62
+ private
63
+
64
+ def find_or_initialize_and_do(meth, options)
65
+ record = nil
66
+ transaction do
67
+ record = first || new(options)
68
+ yield record if block_given?
69
+ record.send(meth) if meth
70
+ end
71
+ return record
72
+ end
73
+
74
+ def create_or_update_and_do(meth, options)
75
+ record = nil
76
+ transaction do
77
+ record = first || new
78
+ record.attributes = options
79
+ yield record if block_given?
80
+ record.send(meth) if meth
81
+ end
82
+ return record
83
+ end
84
+ end
85
+
86
+ ActiveRecord::Relation.send :include, FindOrCreateOnScopes
@@ -0,0 +1,87 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe FindOrCreateOnScopes do
4
+ {
5
+ :find_or_create => :save,
6
+ :find_or_create! => :save!,
7
+ :find_or_initialize => nil
8
+ }.each do |meth, saver|
9
+ describe "##{meth}" do
10
+ it "should find an object if a matching one exists" do
11
+ record = Option.create!(name: 'foo', value: 'bar')
12
+ Option.where(name: 'foo').send(meth, value: 'bar2').should eql(record)
13
+ end
14
+
15
+ it "should create an object if a matching one does not exist" do
16
+ Option.create!(name: 'foo2', value: 'bar2')
17
+ record = Option.where(name: 'foo').send(meth, value: 'bar2')
18
+ record.name.should eql('foo')
19
+ record.value.should eql('bar2')
20
+ end
21
+
22
+ it "should do so in a transaction" do
23
+ Option.should_receive(:transaction)
24
+ Option.where(name: 'foo').send(meth, value: 'bar')
25
+ end
26
+
27
+ it "should yield the object to the block" do
28
+ record = Option.where(name: 'foo').send(meth, value: 'bar') { |u| u.field = "foobar" }
29
+ record.field.should eql('foobar')
30
+
31
+ record = Option.where(name: 'foo').send(meth, value: 'bar') { |u| u.field = "foobar2" }
32
+ record.field.should eql('foobar2')
33
+ end
34
+
35
+ if saver then
36
+ it "should call #{saver}" do
37
+ record = Option.new
38
+ Option.stub!(:new).and_return(record)
39
+ record.should_receive(saver).once.and_return(true)
40
+ Option.where(name: 'foo').send(meth, value: 'bar').should eql(record)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ {
47
+ :create_or_update => :save,
48
+ :create_or_update! => :save!,
49
+ }.each do |meth, saver|
50
+ describe "##{meth}" do
51
+ it "should find an object and update it if a matching one exists" do
52
+ record = Option.create!(name: 'foo', value: 'bar')
53
+ Option.where(name: 'foo').send(meth, value: 'bar2').should eql(record)
54
+ record.reload.value.should eql('bar2')
55
+ end
56
+
57
+ it "should create an object if a matching one does not exist" do
58
+ Option.create!(name: 'foo2', value: 'bar2')
59
+ record = Option.where(name: 'foo').send(meth, value: 'bar')
60
+ record.name.should eql('foo')
61
+ record.value.should eql('bar')
62
+ end
63
+
64
+ it "should do so in a transaction" do
65
+ Option.should_receive(:transaction)
66
+ Option.where(name: 'foo').send(meth, value: 'bar')
67
+ end
68
+
69
+ it "should yield the object to the block" do
70
+ record = Option.where(name: 'foo').send(meth, value: 'bar') { |u| u.field = "foobar" }
71
+ record.field.should eql('foobar')
72
+
73
+ record = Option.where(name: 'foo').send(meth, value: 'bar') { |u| u.field = "foobar2" }
74
+ record.field.should eql('foobar2')
75
+ end
76
+
77
+ if saver then
78
+ it "should call #{saver}" do
79
+ record = Option.new
80
+ Option.stub!(:new).and_return(record)
81
+ record.should_receive(saver).and_return(true)
82
+ Option.where(name: 'foo').send(meth, value: 'bar').should eql(record)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,22 @@
1
+ Bundler.require :default, :test
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+
6
+ require 'find_or_create_on_scopes'
7
+
8
+ ActiveRecord::Base.establish_connection(
9
+ adapter: 'sqlite3',
10
+ database: 'test.sqlite'
11
+ )
12
+
13
+ class Option < ActiveRecord::Base
14
+ attr_accessor :field
15
+ end
16
+
17
+ RSpec.configure do |config|
18
+ config.before(:each) do
19
+ Option.connection.execute "DROP TABLE IF EXISTS options"
20
+ Option.connection.execute "CREATE TABLE options (id INTEGER PRIMARY KEY ASC, name VARCHAR(127) NOT NULL, value VARCHAR(255))"
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: find_or_create_on_scopes
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Tim Morgan
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-10-29 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activerecord
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ prerelease: false
32
+ version_requirements: *id001
33
+ description: Adds methods to ActiveRecord for conditionally finding, creating, or updating records.
34
+ email: git@timothymorgan.info
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - LICENSE
41
+ - README.textile
42
+ files:
43
+ - .document
44
+ - .gitignore
45
+ - .rspec
46
+ - Gemfile
47
+ - Gemfile.lock
48
+ - LICENSE
49
+ - README.textile
50
+ - Rakefile
51
+ - VERSION
52
+ - find_or_create_on_scopes.gemspec
53
+ - lib/find_or_create_on_scopes.rb
54
+ - spec/find_or_create_on_scopes_spec.rb
55
+ - spec/spec_helper.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/riscfuture/find_or_create_on_scopes
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 1
72
+ - 9
73
+ version: "1.9"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project:
85
+ rubygems_version: 1.3.7
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: find_or_create-type methods on ActiveRecord scopes
89
+ test_files:
90
+ - spec/find_or_create_on_scopes_spec.rb
91
+ - spec/spec_helper.rb