console_update 0.1.2

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.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT LICENSE
2
+
3
+ Copyright (c) 2009 Gabriel Horner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,112 @@
1
+ == Description
2
+
3
+ Updates records from the console via your preferred editor. You can update a record's columns as
4
+ well as <i>any attribute</i> that has accessor methods. Records are edited via a temporary file and
5
+ once saved, the records are updated. Records go through a filter before and after editing the file. Yaml is
6
+ the default filter, but you can define your own filter simply with a module and 2 expected methods.
7
+ See ConsoleUpdate::Filter for more details.
8
+
9
+ == Install
10
+
11
+ Install as a gem
12
+
13
+ bash> gem install console_update
14
+
15
+ # add in config/environment.rb
16
+ config.gem "console_update"
17
+
18
+ Or as a plugin
19
+
20
+ bash> script/plugin install git://github.com/cldwalker/console_update.git
21
+
22
+ == Examples
23
+
24
+ For a given model Url, update your records as you please:
25
+
26
+ bash> script/console
27
+
28
+ # Update a record from the object
29
+ >> Url.first.console_update
30
+
31
+ # Update a group of records
32
+ >> records = Url.all :limit=>10
33
+ >> Url.console_update records
34
+
35
+ # Find and update by a given id
36
+ >> Url.find_and_console_update 10
37
+
38
+ # Update through any named_scope ie tagged_with()
39
+ >> Url.tagged_with("sweetness").console_update
40
+
41
+ == Setup
42
+
43
+ Define your editor if not already picked up by environment variable $EDITOR:
44
+
45
+ ConsoleUpdate.editor = 'vim'
46
+
47
+ Configure model(s) to update from the console:
48
+
49
+ class Url
50
+ can_console_update
51
+ end
52
+
53
+ By default, can_console_update() has sensical defaults for what attributes to update.
54
+ But you can setup your own defaults as needed:
55
+
56
+ can_console_update :only=>%w{column1 column2 relation_accessor1}
57
+ can_console_update :except=>%w{column2}
58
+
59
+ To use the named_scope chaining, enable it once.
60
+ ConsoleUpdate.enable_named_scope
61
+
62
+ == More Examples
63
+
64
+ Although console_update() uses the default editable columns, it can take options to override
65
+ these as needed. Note these options can be passed to any of the console_update-like methods shown
66
+ above:
67
+
68
+ records = Url.all :limit=>100
69
+ # Only edit this one attribute
70
+ Url.console_update records, :only=>%w{description}
71
+ # Edit all the default attributes except this one
72
+ Url.console_update records, :except=>%w{description}
73
+
74
+ As mentioned above, any attribute can be edited. This means it's possible to edit associated
75
+ values as well as column values.
76
+
77
+ Say we have a Url that has many tags and accessor methods to edit them ie tag_list() and
78
+ tag_list=():
79
+
80
+ @url.tag_list = ['tag1', 'tag2']
81
+ @url.save
82
+ @url.tag_list # =>['tag1', 'tag2']
83
+
84
+ By simply passing 'tag_list' as another attribute to console_update() or can_console_update(),
85
+ we can edit these associated values:
86
+ class Url
87
+ can_console_update :only=>%w{column1 column2 tag_list}
88
+ end
89
+
90
+ Url.console_update records, :only=>%w{column1 column2 tag_list}
91
+
92
+ == Caveats
93
+ So should you be updating production records with this plugin? Yes and no.
94
+ Yes, if you're updating some simple string/text values. If editing more complex objects
95
+ ie non-string objects and associated objects, try edge cases to ensure the updates work as expected.
96
+ Although this plugin already comes with decent tests, I'm always open to patches for edge cases I
97
+ may have missed.
98
+
99
+ == Motivation
100
+ The need for editing speed in my {console-based project}[http://github.com/cldwalker/tag-tree].
101
+
102
+ == Issues/Bugs
103
+ Please report them {on github}[http://github.com/cldwalker/console_update/issues].
104
+
105
+ == Links
106
+ * http://tagaholic.me/2009/02/28/Console-Update-With-Your-Editor.html
107
+
108
+ == Todo
109
+
110
+ * Have a config file as an alternative configuration method which
111
+ doesn't clutter models with can_console_update() calls.
112
+ * Make ORM-agnostic.
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ begin
5
+ require 'rcov/rcovtask'
6
+
7
+ Rcov::RcovTask.new do |t|
8
+ t.libs << 'test'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ t.rcov_opts = ["-T -x '/Library/Ruby/*'"]
11
+ t.verbose = true
12
+ end
13
+ rescue LoadError
14
+ puts "Rcov not available. Install it for rcov-related tasks with: sudo gem install rcov"
15
+ end
16
+
17
+ begin
18
+ require 'jeweler'
19
+ Jeweler::Tasks.new do |s|
20
+ s.name = "console_update"
21
+ s.summary = "A rails plugin which allows you to edit your database records via the console and your favorite editor."
22
+ s.description = "Updates records from the console via your preferred editor. You can update a record's columns as well as any attribute that has accessor methods. Records are edited via a temporary file and once saved, the records are updated. Records go through a filter before and after editing the file. Yaml is the default filter, but you can define your own filters."
23
+ s.email = "gabriel.horner@gmail.com"
24
+ s.homepage = "http://tagaholic.me/console_update/"
25
+ s.authors = ["Gabriel Horner"]
26
+ s.rubyforge_project = 'tagaholic'
27
+ s.has_rdoc = true
28
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
29
+ s.files = FileList["Rakefile", "VERSION.yml", "README.rdoc", "init.rb", "LICENSE.txt", "{rails,lib,test}/**/*"]
30
+ end
31
+
32
+ rescue LoadError
33
+ puts "Jeweler not available. Install it for jeweler-related tasks with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
34
+ end
35
+
36
+ Rake::TestTask.new do |t|
37
+ t.libs << 'lib'
38
+ t.pattern = 'test/**/*_test.rb'
39
+ t.verbose = false
40
+ end
41
+
42
+ Rake::RDocTask.new do |rdoc|
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = 'test'
45
+ rdoc.options << '--line-numbers' << '--inline-source'
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
49
+
50
+ task :default => :test
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 2
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'rails', 'init.rb')
@@ -0,0 +1,16 @@
1
+ require 'yaml'
2
+
3
+ module ConsoleUpdate
4
+ class Filter
5
+ module Yaml
6
+ def string_to_hashes(string)
7
+ YAML::load(string)
8
+ end
9
+
10
+ def hashes_to_string(hashes)
11
+ hashes.to_yaml
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,40 @@
1
+ require 'console_update/filter/yaml'
2
+
3
+ module ConsoleUpdate
4
+ # A filter converts database records to a string and vice versa.
5
+ # A database record is represented as a hash of attributes with stringified keys.
6
+ # Each hash should have an id attribute to map it to its database record.
7
+ # To create your own filter, create a module in the ConsoleUpdate::Filter namespace.
8
+ # Although the name of the module can be anything, if only the first letter is capitalized
9
+ # then a simple lowercase name of the filter can be used with ConsoleUpdate.filter() (ie :yaml
10
+ # instead of Yaml). For now, new filters have to be manually loaded/required.
11
+ #
12
+ # A filter should have two methods defined, string_to_hashes() and hashes_to_string().
13
+ # For a good example of a filter see ConsoleUpdate::Filter::Yaml.
14
+ #
15
+ class Filter
16
+ class AbstractMethodError < StandardError; end
17
+ class FilterNotFoundError < StandardError; end
18
+
19
+ # Creates a filter given a type.
20
+ def initialize(filter_type)
21
+ @filter_type = filter_type
22
+ begin
23
+ filter_module = self.class.const_get(filter_type.to_s.gsub(/^([a-z])/) {|e| $1.upcase })
24
+ self.extend(filter_module)
25
+ rescue NameError
26
+ raise FilterNotFoundError
27
+ end
28
+ end
29
+
30
+ # Takes an an array of hashes representing database records and converts them to a string for editing.
31
+ def hashes_to_string(hashes)
32
+ raise AbstractMethodError
33
+ end
34
+
35
+ # Takes the string from the updated file and converts back to an array of hashes.
36
+ def string_to_hashes(string)
37
+ raise AbstractMethodError
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,21 @@
1
+ module ConsoleUpdate
2
+
3
+ # Adds a global scoped method, console_update(), which can be chained
4
+ # to the end of any named scopes.
5
+ # For example, if Url has a named scope :tagged_with:
6
+ # Url.tagged_with('physics').console_update
7
+ def self.enable_named_scope
8
+ ActiveRecord::NamedScope::Scope.send :include, NamedScope
9
+ end
10
+
11
+ module NamedScope #:nodoc:
12
+ def console_update(options={})
13
+ records = send(:proxy_found)
14
+ unless records.empty?
15
+ records[0].class.console_update(records, options)
16
+ else
17
+ []
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,149 @@
1
+ current_dir = File.dirname(__FILE__)
2
+ $:.unshift(current_dir) unless $:.include?(current_dir) || $:.include?(File.expand_path(current_dir))
3
+ require 'tempfile'
4
+ require 'console_update/named_scope'
5
+ require 'console_update/filter'
6
+
7
+ module ConsoleUpdate
8
+ class <<self; attr_accessor(:filter, :editor); end
9
+ def self.included(base) #:nodoc:
10
+ @filter = :yaml
11
+ @editor = ENV["EDITOR"]
12
+ base.extend ClassMethods
13
+ end
14
+
15
+ module ClassMethods
16
+ # Enable a model to be updated via the console and an editor. By default editable
17
+ # attributes are columns with text, boolean or integer-like values.
18
+ # ==== Options:
19
+ # [:only] Sets these attributes as the default editable attributes.
20
+ # [:except] Sets the default editable attributes as normal except for these attributes.
21
+ # [:editor] Overrides global editor for just this model.
22
+ def can_console_update(options={})
23
+ cattr_accessor :console_editor
24
+ self.console_editor = options[:editor] || ConsoleUpdate.editor
25
+
26
+ cattr_accessor :default_editable_attributes
27
+ if options[:only]
28
+ self.default_editable_attributes = options[:only]
29
+ elsif options[:except]
30
+ self.default_editable_attributes = self.column_names.select {|e| !options[:except].include?(e) }
31
+ else
32
+ self.default_editable_attributes = get_default_editable_attributes
33
+ end
34
+
35
+ extend SingletonMethods
36
+ send :include, InstanceMethods
37
+ end
38
+
39
+ private
40
+ def default_types_to_exclude
41
+ [:datetime, :timestamp, :binary, :time, :timestamp]
42
+ end
43
+
44
+ def get_default_editable_attributes
45
+ self.columns.select {|e| !default_types_to_exclude.include?(e.type) }.map(&:name)
46
+ end
47
+ end
48
+
49
+ module SingletonMethods
50
+ # This is the main method for updating records.
51
+ # All other update-like methods pass their options through here.
52
+ # ==== Options:
53
+ # [:only] Only edit these attributes.
54
+ # [:except] Edit default attributes except for these.
55
+ # Examples:
56
+ # records = Url.all :limit=>10
57
+ # Url.console_update records
58
+ # Url.console_update records, :only=>%w{column1}
59
+ # Url.console_update records, :except=>%w{column1}
60
+ def console_update(records, options={})
61
+ begin
62
+ editable_attributes_array = records.map {|e| e.console_editable_attributes(options) }
63
+ editable_string = filter.hashes_to_string(editable_attributes_array)
64
+ new_attributes_array = editor_update(editable_string)
65
+ records.each do |record|
66
+ if (record_attributes = new_attributes_array.detect {|e| e['id'] == record.id })
67
+ record.update_console_attributes(record_attributes)
68
+ end
69
+ end
70
+ rescue ConsoleUpdate::Filter::AbstractMethodError
71
+ puts "Undefined filter method for #{ConsoleUpdate::filter} filter"
72
+ rescue Test::Unit::AssertionFailedError=>e
73
+ raise e
74
+ rescue Exception=>e
75
+ puts "Some record(s) didn't update because of this error: #{e}"
76
+ ensure
77
+ #this attribute should only last duration of method
78
+ reset_editable_attribute_names
79
+ end
80
+ end
81
+
82
+ def filter #:nodoc:
83
+ @filter ||= ConsoleUpdate::Filter.new(ConsoleUpdate.filter)
84
+ end
85
+
86
+ # Console updates a record given an id.
87
+ def find_and_console_update(id, options={})
88
+ console_update([find(id)], options)
89
+ end
90
+
91
+ # :stopdoc:
92
+ def editor_update(string)
93
+ tmpfile = Tempfile.new('console_update')
94
+ File.open(tmpfile.path, 'w+') {|f| f.write(string)}
95
+ system(console_editor, tmpfile.path)
96
+ updated_string = File.read(tmpfile.path)
97
+ filter.string_to_hashes(updated_string)
98
+ end
99
+
100
+ def reset_editable_attribute_names; @editable_attribute_names = nil ; end
101
+
102
+ def editable_attribute_names(options={})
103
+ unless @editable_attribute_names
104
+ @editable_attribute_names = if options[:only]
105
+ options[:only]
106
+ elsif options[:except]
107
+ default_editable_attributes - options[:except]
108
+ else
109
+ default_editable_attributes
110
+ end
111
+ end
112
+ @editable_attribute_names
113
+ end
114
+ # :startdoc:
115
+ end
116
+
117
+ module InstanceMethods
118
+ # Console updates the object.
119
+ def console_update(options={})
120
+ self.class.console_update([self], options)
121
+ end
122
+
123
+ # :stopdoc:
124
+ def update_console_attributes(new_attributes)
125
+ # delete if value is the same or is an attribute that isn't supposed to be edited
126
+ new_attributes.delete_if {|k,v|
127
+ attributes[k] == v || !self.class.editable_attribute_names.include?(k)
128
+ }
129
+ new_attributes.each do |k, v|
130
+ send("#{k}=", v)
131
+ end
132
+ save
133
+ end
134
+
135
+ def get_console_attributes(attribute_names)
136
+ attribute_names.inject({}) {|h,e|
137
+ h[e] = attributes.has_key?(e) ? attributes[e] : send(e)
138
+ h
139
+ }
140
+ end
141
+
142
+ def console_editable_attributes(options)
143
+ fresh_attributes = get_console_attributes(self.class.editable_attribute_names(options))
144
+ fresh_attributes['id'] ||= self.id
145
+ fresh_attributes
146
+ end
147
+ #:startdoc:
148
+ end
149
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'console_update'
2
+ ActiveRecord::Base.send :include, ConsoleUpdate
@@ -0,0 +1,133 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class ConsoleUpdateTest < Test::Unit::TestCase
4
+ def stub_editor_update_with_records(new_records, options={})
5
+ Bird.stub!(:system) { |editor, file|
6
+ if options[:expected_attributes]
7
+ YAML::load_file(file)[0].keys.sort.should == options[:expected_attributes]
8
+ end
9
+ File.open(file, 'w+') {|f| f.write(new_records.to_yaml)}
10
+ }
11
+ end
12
+
13
+ def create_big_bird(attributes={})
14
+ @big_bird = Bird.create({:name=>"big bird"}.update(attributes))
15
+ end
16
+
17
+ context "can_console_update" do
18
+ test "sets default_editable_attributes" do
19
+ Bird.column_names.include?('bin').should be(true)
20
+ Bird.default_editable_attributes.empty?.should be(false)
21
+ Bird.default_editable_attributes.include?('bin').should be(false)
22
+ end
23
+
24
+ test "sets default_editable_attributes with only option" do
25
+ Bird.can_console_update :only=>['description']
26
+ Bird.default_editable_attributes.should == ['description']
27
+ end
28
+
29
+ test "sets default_editable_attributes with except option" do
30
+ Bird.can_console_update :except=>['description']
31
+ Bird.default_editable_attributes.sort.should == ["bin", "created_at", "id", "name", "nickname", "updated_at"]
32
+ end
33
+
34
+ test "sets console_editor with editor option" do
35
+ Bird.can_console_update :editor=>'vi'
36
+ Bird.console_editor.should == 'vi'
37
+ end
38
+ end
39
+
40
+ context "console_update" do
41
+ before(:all) {|e| Bird.can_console_update }
42
+ before(:each) {|e| Bird.delete_all }
43
+
44
+ test "updates multiple records" do
45
+ create_big_bird
46
+ @dodo = Bird.create(:name=>"dodo")
47
+ new_records = [@big_bird.attributes.update('name'=>'big birded'), @dodo.attributes.update('name'=>'dudu')]
48
+ stub_editor_update_with_records(new_records)
49
+ Bird.console_update([@big_bird, @dodo])
50
+ @big_bird.reload.name.should == 'big birded'
51
+ @dodo.reload.name.should == 'dudu'
52
+ end
53
+
54
+ test "doesn't modify missing attributes" do
55
+ create_big_bird(:nickname=>'doofus')
56
+ # this record is missing all it's attributes except name
57
+ new_records = [{'name'=>'big birded', 'id'=>@big_bird.id}]
58
+ stub_editor_update_with_records(new_records)
59
+ Bird.console_update([@big_bird])
60
+ @big_bird.reload.nickname.should == 'doofus'
61
+ @big_bird.name.should == 'big birded'
62
+ end
63
+
64
+ test "updates attr_protected column" do
65
+ create_big_bird
66
+ new_records = [@big_bird.attributes.update('description'=>"it's big")]
67
+ stub_editor_update_with_records(new_records)
68
+ Bird.console_update([@big_bird])
69
+ @big_bird.reload.description.should == "it's big"
70
+ end
71
+
72
+ test "rescues exception" do
73
+ create_big_bird
74
+ Bird.stub!(:system) {|*args| raise "error" }
75
+ capture_stdout {
76
+ Bird.console_update([@big_bird])
77
+ }
78
+ end
79
+
80
+ test "via instance method" do
81
+ create_big_bird
82
+ stub_editor_update_with_records([@big_bird.attributes.update('name'=>'small bird')])
83
+ @big_bird.console_update
84
+ @big_bird.reload.name.should == 'small bird'
85
+ end
86
+
87
+ test "via find_and_console_update" do
88
+ create_big_bird
89
+ stub_editor_update_with_records([@big_bird.attributes.update('name'=>'small bird')])
90
+ Bird.find_and_console_update(@big_bird.id)
91
+ @big_bird.reload.name.should == 'small bird'
92
+ end
93
+
94
+ test "with only option only edits those columns" do
95
+ create_big_bird(:description=>"something")
96
+ stub_editor_update_with_records([@big_bird.attributes.update('name'=>'big birded')], :expected_attributes=>['id', 'name'])
97
+ Bird.console_update([@big_bird], :only=>['name'])
98
+ @big_bird.reload.name.should == 'big birded'
99
+ end
100
+
101
+ test "with except option edits all columns except those columns" do
102
+ create_big_bird(:description=>"something")
103
+ expected_attributes = ["id", "name", "nickname"]
104
+ stub_editor_update_with_records([@big_bird.attributes.update('name'=>'big birded')], :expected_attributes=>expected_attributes)
105
+ Bird.console_update([@big_bird], :except=>['description'])
106
+ @big_bird.reload.name.should == 'big birded'
107
+ end
108
+
109
+ test "sets a non column attribute" do
110
+ create_big_bird
111
+ stub_editor_update_with_records([{'tag_list'=>["yellow"], 'id'=>@big_bird.id}], :expected_attributes=>["id","tag_list"])
112
+ Bird.console_update([@big_bird], :only=>["tag_list"] )
113
+ @big_bird.tag_list.should == ['yellow']
114
+ end
115
+
116
+ test "updates and deletes extra attributes added in editor" do
117
+ create_big_bird
118
+ stub_editor_update_with_records([{'name'=>'big birded','nickname'=>'doofird', 'id'=>@big_bird.id}])
119
+ Bird.console_update([@big_bird], :only=>['name'])
120
+ @big_bird.reload.nickname.should_not == 'doofird'
121
+ end
122
+ end
123
+
124
+ test "editable_attribute_names expire per console_update" do
125
+ create_big_bird
126
+ stub_editor_update_with_records([{'name'=>'big birded', 'id'=>@big_bird.id}], :expected_attributes=>["id", "name"])
127
+ Bird.console_update([@big_bird], :only=>['name'])
128
+ @big_bird.instance_eval("@editable_attribute_names").should == nil
129
+ #these expected_attributes would fail if @editable_attribute_names wasn't reset for each console_update()
130
+ stub_editor_update_with_records([{'description'=>'big birded','id'=>@big_bird.id}], :expected_attributes=>["description", "id"])
131
+ Bird.console_update([@big_bird], :only=>['description'])
132
+ end
133
+ end
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ #test filter
4
+ module ConsoleUpdate
5
+ class Filter
6
+ module Test
7
+ end
8
+ end
9
+ end
10
+
11
+ class ConsoleUpdate::FilterTest < Test::Unit::TestCase
12
+ test "incorrect filter name raises FilterNotFoundError" do
13
+ assert_raises(ConsoleUpdate::Filter::FilterNotFoundError) {
14
+ ConsoleUpdate::Filter.new(:blah)
15
+ }
16
+ end
17
+
18
+ test "filter without proper methods raises AbstractMethodError" do
19
+ assert_raises(ConsoleUpdate::Filter::AbstractMethodError) {
20
+ ConsoleUpdate::Filter.new(:test).string_to_hashes('blah')
21
+ }
22
+ end
23
+
24
+ test "extends filter for a non-lowercase filter name correctly" do
25
+ filter_meta_class = ConsoleUpdate::Filter.new("Test").instance_eval("class<<self; self;end")
26
+ filter_meta_class.ancestors.include?(ConsoleUpdate::Filter::Test).should be(true)
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class ConsoleUpdate::NamedScopeTest < Test::Unit::TestCase
4
+ test "chained console_update calls actual named_scope" do
5
+ big_bird = Bird.create({:name=>"big bird"})
6
+ Bird.stub!(:system) { |editor, file|
7
+ YAML::load_file(file)[0].keys.sort.should == Bird.default_editable_attributes.sort
8
+ }
9
+ ConsoleUpdate.enable_named_scope
10
+ Bird.just_big_bird.console_update
11
+ end
12
+ end
data/test/schema.rb ADDED
@@ -0,0 +1,8 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :birds do |t|
3
+ t.string :name, :nickname
4
+ t.text :description
5
+ t.timestamps
6
+ t.binary :bin
7
+ end
8
+ end
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'activerecord'
3
+ require 'test/unit'
4
+ require 'context' #gem install jeremymcanally-context -s http://gems.github.com
5
+ require 'matchy' #gem install jeremymcanally-matchy -s http://gems.github.com
6
+ require 'stump' #gem install jeremymcanally-stump -s http://gems.github.com
7
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
8
+ require File.join(File.dirname(__FILE__), '..', 'init')
9
+
10
+ #Setup logger
11
+ require 'logger'
12
+ # ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), "test.log"))
13
+ ActiveRecord::Base.logger = Logger.new(STDERR)
14
+ ActiveRecord::Base.logger.level = Logger::WARN
15
+
16
+ #Setup db
17
+ ActiveRecord::Base.configurations = {'sqlite3' => {:adapter => 'sqlite3', :database => ':memory:'}}
18
+ ActiveRecord::Base.establish_connection('sqlite3')
19
+
20
+ #Define schema
21
+ require File.join(File.dirname(__FILE__), 'schema')
22
+ class Bird < ActiveRecord::Base
23
+ named_scope :just_big_bird, :conditions=>{:name=>'big bird'}
24
+ attr_protected :description
25
+ attr_accessor :tag_list
26
+ can_console_update
27
+ end
28
+
29
+ class Test::Unit::TestCase
30
+ def capture_stdout(&block)
31
+ original_stdout = $stdout
32
+ $stdout = fake = StringIO.new
33
+ begin
34
+ yield
35
+ ensure
36
+ $stdout = original_stdout
37
+ end
38
+ fake.string
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: console_update
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel Horner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-22 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Updates records from the console via your preferred editor. You can update a record's columns as well as any attribute that has accessor methods. Records are edited via a temporary file and once saved, the records are updated. Records go through a filter before and after editing the file. Yaml is the default filter, but you can define your own filters.
17
+ email: gabriel.horner@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE.txt
24
+ - README.rdoc
25
+ files:
26
+ - LICENSE.txt
27
+ - README.rdoc
28
+ - Rakefile
29
+ - VERSION.yml
30
+ - init.rb
31
+ - lib/console_update.rb
32
+ - lib/console_update/filter.rb
33
+ - lib/console_update/filter/yaml.rb
34
+ - lib/console_update/named_scope.rb
35
+ - rails/init.rb
36
+ - test/console_update_test.rb
37
+ - test/filter_test.rb
38
+ - test/named_scope_test.rb
39
+ - test/schema.rb
40
+ - test/test_helper.rb
41
+ has_rdoc: true
42
+ homepage: http://tagaholic.me/console_update/
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --charset=UTF-8
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project: tagaholic
65
+ rubygems_version: 1.3.5
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: A rails plugin which allows you to edit your database records via the console and your favorite editor.
69
+ test_files:
70
+ - test/console_update_test.rb
71
+ - test/filter_test.rb
72
+ - test/named_scope_test.rb
73
+ - test/schema.rb
74
+ - test/test_helper.rb