discourse-seed-fu 2.3.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 61855739941fabc2a6be38a7e1664a8827a9a76033f2ac775701fd8675c1463b
4
+ data.tar.gz: fed1eb46a4b37cb8e0cc1cfc394072fd6bc53392ba7010ffe641d8d1f537f5f1
5
+ SHA512:
6
+ metadata.gz: df3cc7391961357d0a83cf838a5ce52aee68f84d0917714466b5e2d01149387beab4b23457516a34cde120206a19be309f22686e72a2f694fac2389e15355dbe
7
+ data.tar.gz: c50ea60de121ade1ce94f7045d403aaa823534357a1d22e3c5041a606d7d435d5eb43fba414a15945f3e99e610fb23daed7df67cee77b5ca0c4862e8a1aacc37
data/CHANGELOG.md ADDED
@@ -0,0 +1,121 @@
1
+ Version 2.3.8
2
+ -------------
3
+
4
+ Bug fixes:
5
+
6
+ * Restored support for PostgreSQL with Rails versions before 5.0.0 broken in Seed Fu 2.3.7.
7
+
8
+ Version 2.3.7
9
+ -------------
10
+
11
+ Features:
12
+
13
+ * Postgresql >= 10.0 support
14
+
15
+ Version 2.3.6
16
+ -------------
17
+
18
+ Features:
19
+
20
+ * Rails 5.0 support
21
+
22
+ Version 2.3.5
23
+ -------------
24
+
25
+ Features:
26
+
27
+ * Rails 4.2 support
28
+
29
+ Version 2.3.3
30
+ -------------
31
+
32
+ Features:
33
+
34
+ * Capistrano v3 support (by @shishi)
35
+
36
+ Version 2.3.2
37
+ -------------
38
+
39
+ Features:
40
+
41
+ * Documentation improvements (by @george, @kenips, @joshuapinter)
42
+ * Fix documentation of seed_once method (by weedySeaDragon)
43
+ * Allow to seed data with an id < 1 (by @SamSaffron, @aserafin)
44
+ * Seeds work on postgresql when there is no primary key or if primary key has no sequence assigned (by @aserafin)
45
+
46
+ Version 2.3.1
47
+ -------------
48
+
49
+ Features:
50
+
51
+ * Rails 4.1 support added.
52
+ * Capistrano task included. (by @linjunpop)
53
+
54
+ Version 2.3.0
55
+ -------------
56
+
57
+ Features:
58
+
59
+ * Rails 4.0.X support added. (by @tkhr, @DanielWright)
60
+
61
+ Version 2.2.0
62
+ -------------
63
+
64
+ * Rails 3.2 support
65
+
66
+ Version 2.1.0
67
+ -------------
68
+
69
+ Features:
70
+
71
+ * Deprecations removed
72
+
73
+ * Rails 3.1 support added, Rails 3.0 support removed (please use 2.0.X line with 3.0)
74
+
75
+ Version 2.0.1
76
+ -------------
77
+
78
+ Bug fixes:
79
+
80
+ * Update the primary key sequence in PostgreSQL tables after seeding data. This ensures that id conflicts do not occur when records are subsequently added to the table.
81
+
82
+ * Raise ActiveRecord::RecordNotSaved if any of the saves fail (but they won't fail due to validation since saves are done without validation, so this guards against callbacks failing etc.)
83
+
84
+ Version 2.0.0
85
+ -------------
86
+
87
+ Features:
88
+
89
+ * Depends only on Active Record, not the whole of Rails
90
+
91
+ * The `Model.seed_many` syntax is now supported by `Model.seed`, and `Model.seed_many` is deprecated
92
+
93
+ * `Model.seed` supports adding multiple records without an explicit array argument. I.e. the following are equivalent:
94
+
95
+ Model.seed([
96
+ { :name => "Jon" },
97
+ { :name => "Emily" }
98
+ ])
99
+
100
+ Model.seed(
101
+ { :name => "Jon" },
102
+ { :name => "Emily }
103
+ )
104
+
105
+ * A side-effect of the above is another option for single seeds:
106
+
107
+ Model.seed(:name => "Jon")
108
+
109
+ * The `SEED` option to `rake db:seed_fu` is deprecated, and replaced by `FILTER` which works the same way.
110
+
111
+ * Added `SeedFu.quiet` boolean option, set to `true` if you don't want any output from Seed Fu.
112
+
113
+ * Added `SeedFu.fixture_paths`. Set to an array of paths to look for seed files in. Defaults to `["db/fixtures"]` in general, or `["#{Rails.root}/db/fixtures", "#{Rails.root}/db/fixtures/#{Rails.env}"]` when Seed Fu is installed as a Rails plugin.
114
+
115
+ * Added `SeedFu.seed` method which is basically a method equivalent of running `rake db:seed_fu` (the rake task now just basically called `SeedFu.seed`)
116
+
117
+ * Simplified and changed the `SeedFu::Writer` API, see docs for details
118
+
119
+ Bug fixes:
120
+
121
+ * Fix Rails 3 deprecation warnings and make seed-fu fully compatible with being installed as a gem
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2010 Michael Bleigh
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.md ADDED
@@ -0,0 +1,173 @@
1
+ Seed Fu
2
+ =======
3
+
4
+ Seed Fu is an attempt to once and for all solve the problem of inserting and maintaining seed data in a database. It uses a variety of techniques gathered from various places around the web and combines them to create what is hopefully the most robust seed data system around.
5
+
6
+ Warning: API Changes
7
+ --------------------
8
+
9
+ Version 2.0.0 of Seed Fu introduced API changes. `Seed::Writer` was been completely overhauled and will require you to update your scripts. Some other deprecations were introduced, and support is fully removed in version 2.1.0. Please see the [CHANGELOG](CHANGELOG.md) for details.
10
+
11
+ The API documentation is available in full at [http://rubydoc.info/github/mbleigh/seed-fu/master/frames](http://rubydoc.info/github/mbleigh/seed-fu/master/frames).
12
+
13
+ Basic Example
14
+ -------------
15
+
16
+ ### In `db/fixtures/users.rb`
17
+
18
+ User.seed do |s|
19
+ s.id = 1
20
+ s.login = "jon"
21
+ s.email = "jon@example.com"
22
+ s.name = "Jon"
23
+ end
24
+
25
+ User.seed do |s|
26
+ s.id = 2
27
+ s.login = "emily"
28
+ s.email = "emily@example.com"
29
+ s.name = "Emily"
30
+ end
31
+
32
+ ### To load the data:
33
+
34
+ $ rake db:seed_fu
35
+ == Seed from /path/to/app/db/fixtures/users.rb
36
+ - User {:id=>1, :login=>"jon", :email=>"jon@example.com", :name=>"Jon"}
37
+ - User {:id=>2, :login=>"emily", :email=>"emily@example.com", :name=>"Emily"}
38
+
39
+ Installation
40
+ ------------
41
+
42
+ ### Rails 3.1, 3.2, 4.0, 4.1, 4.2, 5.0
43
+
44
+ Just add `gem 'seed-fu', '~> 2.3'` to your `Gemfile`
45
+
46
+ Seed Fu depends on Active Record, but doesn't have to be used with a full Rails app. Simply load and require the `seed-fu` gem and you're set.
47
+
48
+ ### Rails 3.0
49
+
50
+ The current version is not backwards compatible with Rails 3.0. Please use `gem 'seed-fu', '~> 2.0.0'`.
51
+
52
+ ### Rails 2.3
53
+
54
+ The current version is not backwards compatible with Rails 2.3. Please use `gem 'seed-fu', '~> 1.2.0'`.
55
+
56
+ Constraints
57
+ -----------
58
+
59
+ Constraints are used to identify seeds, so that they can be updated if necessary. For example:
60
+
61
+ Point.seed(:x, :y) do |s|
62
+ s.x = 4
63
+ s.y = 7
64
+ s.name = "Home"
65
+ end
66
+
67
+ The first time this seed is loaded, a `Point` record will be created. Now suppose the name is changed:
68
+
69
+ Point.seed(:x, :y) do |s|
70
+ s.x = 4
71
+ s.y = 7
72
+ s.name = "Work"
73
+ end
74
+
75
+ When this is run, Seed Fu will look for a `Point` based on the `:x` and `:y` constraints provided. It will see that a matching `Point` already exists and so update its attributes rather than create a new record.
76
+
77
+ If you do not want seeds to be updated after they have been created, use `seed_once`:
78
+
79
+ Point.seed_once(:x, :y) do |s|
80
+ s.x = 4
81
+ s.y = 7
82
+ s.name = "Home"
83
+ end
84
+
85
+ The default constraint just checks the `id` of the record.
86
+
87
+ Where to put seed files
88
+ -----------------------
89
+
90
+ By default, seed files are looked for in the following locations:
91
+
92
+ * `#{Rails.root}/db/fixtures` and `#{Rails.root}/db/fixtures/#{Rails.env}` in a Rails app
93
+ * `./db/fixtures` when loaded without Rails
94
+
95
+ You can change these defaults by modifying the `SeedFu.fixture_paths` array.
96
+
97
+ Seed files can be named whatever you like, and are loaded in alphabetical order.
98
+
99
+ Terser syntax
100
+ -------------
101
+
102
+ When loading lots of records, the above block-based syntax can be quite verbose. You can use the following instead:
103
+
104
+ User.seed(:id,
105
+ { :id => 1, :login => "jon", :email => "jon@example.com", :name => "Jon" },
106
+ { :id => 2, :login => "emily", :email => "emily@example.com", :name => "Emily" }
107
+ )
108
+
109
+ Rake task
110
+ ---------
111
+
112
+ Seed files can be run automatically using `rake db:seed_fu`. There are two options which you can pass:
113
+
114
+ * `rake db:seed_fu FIXTURE_PATH=path/to/fixtures` -- Where to find the fixtures
115
+ * `rake db:seed_fu FILTER=users,articles` -- Only run seed files with a filename matching the `FILTER`
116
+
117
+ You can also do a similar thing in your code by calling `SeedFu.seed(fixture_paths, filter)`.
118
+
119
+ Disable output
120
+ --------------
121
+
122
+ To disable output from Seed Fu, set `SeedFu.quiet = true`.
123
+
124
+ Handling large seed files
125
+ -------------------------
126
+
127
+ Seed files can be huge. To handle large files (over a million rows), try these tricks:
128
+
129
+ * Gzip your fixtures. Seed Fu will read .rb.gz files happily. `gzip -9` gives the best compression, and with Seed Fu's repetitive syntax, a 160M file can shrink to 16M.
130
+ * Add lines reading `# BREAK EVAL` in your big fixtures, and Seed Fu will avoid loading the whole file into memory. If you use `SeedFu::Writer`, these breaks are built into your generated fixtures.
131
+ * Load a single fixture at a time with the `FILTER` environment variable
132
+ * If you don't need Seed Fu's ability to update seed with new data, then you may find that [activerecord-import](https://github.com/zdennis/activerecord-import) is faster
133
+
134
+ Generating seed files
135
+ ---------------------
136
+
137
+ If you need to programmatically generate seed files, for example to convert a CSV file into a seed file, then you can use [`SeedFu::Writer`](lib/seed-fu/writer.rb).
138
+
139
+ Capistrano deployment
140
+ ---------------------
141
+
142
+ SeedFu has included Capistrano [deploy script](lib/seed-fu/capistrano.rb), you just need require that
143
+ in `config/deploy.rb`:
144
+
145
+ ```ruby
146
+ require 'seed-fu/capistrano'
147
+
148
+ # Trigger the task after update_code
149
+ after 'deploy:update_code', 'db:seed_fu'
150
+ ```
151
+
152
+ If you use Capistrano3, you should require another file.
153
+
154
+ ```ruby
155
+ require 'seed-fu/capistrano3'
156
+
157
+ # Trigger the task before publishing
158
+ before 'deploy:publishing', 'db:seed_fu'
159
+ ```
160
+
161
+ Bugs / Feature requests
162
+ -----------------------
163
+
164
+ Please report them on [Github Issues](https://github.com/mbleigh/seed-fu/issues).
165
+
166
+ Contributors
167
+ ------------
168
+
169
+ * [Michael Bleigh](http://www.mbleigh.com/) is the original author
170
+ * [Jon Leighton](http://jonathanleighton.com/) is the current maintainer
171
+ * Thanks to [Matthew Beale](https://github.com/mixonic) for his great work in adding the writer, making it faster and better.
172
+
173
+ Copyright © 2008-2010 Michael Bleigh, released under the MIT license
@@ -0,0 +1,36 @@
1
+ require 'active_record'
2
+ require 'active_support/core_ext/module/attribute_accessors'
3
+ require 'seed-fu/railtie' if defined?(Rails) && Rails.version >= "3"
4
+
5
+ module SeedFu
6
+ autoload :VERSION, 'seed-fu/version'
7
+ autoload :Seeder, 'seed-fu/seeder'
8
+ autoload :ActiveRecordExtension, 'seed-fu/active_record_extension'
9
+ autoload :BlockHash, 'seed-fu/block_hash'
10
+ autoload :Runner, 'seed-fu/runner'
11
+ autoload :Writer, 'seed-fu/writer'
12
+
13
+ mattr_accessor :quiet
14
+
15
+ # Set `SeedFu.quiet = true` to silence all output
16
+ @@quiet = false
17
+
18
+ mattr_accessor :fixture_paths
19
+
20
+ # Set this to be an array of paths to directories containing your seed files. If used as a Rails
21
+ # plugin, SeedFu will set to to contain `Rails.root/db/fixtures` and
22
+ # `Rails.root/db/fixtures/Rails.env`
23
+ @@fixture_paths = ['db/fixtures']
24
+
25
+ # Load seed data from files
26
+ # @param [Array] fixture_paths The paths to look for seed files in
27
+ # @param [Regexp] filter If given, only filenames matching this expression will be loaded
28
+ def self.seed(fixture_paths = SeedFu.fixture_paths, filter = nil)
29
+ Runner.new(fixture_paths, filter).run
30
+ end
31
+ end
32
+
33
+ # @public
34
+ class ActiveRecord::Base
35
+ extend SeedFu::ActiveRecordExtension
36
+ end
@@ -0,0 +1,65 @@
1
+ module SeedFu
2
+ module ActiveRecordExtension
3
+ # Load some seed data. There are two ways to do this.
4
+ #
5
+ # Verbose syntax
6
+ # --------------
7
+ #
8
+ # This will seed a single record. The `:id` parameter ensures that if a record already exists
9
+ # in the database with the same id, then it will be updated with the name and age, rather
10
+ # than created from scratch.
11
+ #
12
+ # Person.seed(:id) do |s|
13
+ # s.id = 1
14
+ # s.name = "Jon"
15
+ # s.age = 21
16
+ # end
17
+ #
18
+ # Note that `:id` is the default attribute used to identify a seed, so it need not be
19
+ # specified.
20
+ #
21
+ # Terse syntax
22
+ # ------------
23
+ #
24
+ # This is a more succinct way to load multiple records. Note that both `:x` and `:y` are being
25
+ # used to identify a seed here.
26
+ #
27
+ # Point.seed(:x, :y,
28
+ # { :x => 3, :y => 10, :name => "Home" },
29
+ # { :x => 5, :y => 9, :name => "Office" }
30
+ # )
31
+ def seed(*args, &block)
32
+ SeedFu::Seeder.new(self, *parse_seed_fu_args(args, block)).seed
33
+ end
34
+
35
+ # Has the same syntax as {#seed}, but if a record already exists with the same values for
36
+ # constraining attributes, it will not be updated.
37
+ #
38
+ # @example
39
+ # Person.seed(:id, :id => 1, :name => "Jon") # => Record created
40
+ # Person.seed(:id, :id => 1, :name => "Bob") # => Name changed
41
+ # Person.seed_once(:id, :id => 1, :name => "Harry") # => Name *not* changed
42
+ def seed_once(*args, &block)
43
+ constraints, data = parse_seed_fu_args(args, block)
44
+ SeedFu::Seeder.new(self, constraints, data, :insert_only => true).seed
45
+ end
46
+
47
+ private
48
+
49
+ def parse_seed_fu_args(args, block)
50
+ if block.nil?
51
+ if args.last.is_a?(Array)
52
+ # Last arg is an array of data, so assume the rest of the args are constraints
53
+ data = args.pop
54
+ [args, data]
55
+ else
56
+ # Partition the args, assuming the first hash is the start of the data
57
+ args.partition { |arg| !arg.is_a?(Hash) }
58
+ end
59
+ else
60
+ # We have a block, so assume the args are all constraints
61
+ [args, [SeedFu::BlockHash.new(block).to_hash]]
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,21 @@
1
+ module SeedFu
2
+ # @private
3
+ class BlockHash
4
+ def initialize(proc)
5
+ @hash = {}
6
+ proc.call(self)
7
+ end
8
+
9
+ def to_hash
10
+ @hash
11
+ end
12
+
13
+ def method_missing(method_name, *args, &block)
14
+ if method_name.to_s =~ /^(.*)=$/ && args.length == 1 && block.nil?
15
+ @hash[$1] = args.first
16
+ else
17
+ super
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ Capistrano::Configuration.instance.load do
2
+ namespace :db do
3
+ desc "Load seed data into database"
4
+ task :seed_fu, :roles => :db, :only => { :primary => true } do
5
+ run "cd #{release_path} && bundle exec rake RAILS_ENV=#{rails_env} db:seed_fu"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1 @@
1
+ load File.expand_path('../../tasks/seed_fu_capistrano3.rake', __FILE__)
@@ -0,0 +1,14 @@
1
+ module SeedFu
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ load "tasks/seed_fu.rake"
5
+ end
6
+
7
+ initializer 'seed_fu.set_fixture_paths' do
8
+ SeedFu.fixture_paths = [
9
+ Rails.root.join('db/fixtures').to_s,
10
+ Rails.root.join('db/fixtures/' + Rails.env).to_s
11
+ ]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,73 @@
1
+ require 'zlib'
2
+ require 'active_support/core_ext/array/wrap'
3
+
4
+ module SeedFu
5
+ # Runs seed files.
6
+ #
7
+ # It is not recommended to use this class directly. Instead, use {SeedFu.seed SeedFu.seed}, which creates
8
+ # an instead of {Runner} and calls {#run #run}.
9
+ #
10
+ # @see SeedFu.seed SeedFu.seed
11
+ class Runner
12
+ # @param [Array<String>] fixture_paths The paths where fixtures are located. Will use
13
+ # `SeedFu.fixture_paths` if {nil}. If the argument is not an array, it will be wrapped by one.
14
+ # @param [Regexp] filter If given, only seed files with a file name matching this pattern will
15
+ # be used
16
+ def initialize(fixture_paths = nil, filter = nil)
17
+ @fixture_paths = Array.wrap(fixture_paths || SeedFu.fixture_paths)
18
+ @filter = filter
19
+ end
20
+
21
+ # Run the seed files.
22
+ def run
23
+ puts "\n== Filtering seed files against regexp: #{@filter.inspect}" if @filter && !SeedFu.quiet
24
+
25
+ filenames.each do |filename|
26
+ run_file(filename)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def run_file(filename)
33
+ puts "\n== Seed from #{filename}" unless SeedFu.quiet
34
+
35
+ ActiveRecord::Base.transaction do
36
+ open(filename) do |file|
37
+ chunked_ruby = ''
38
+ file.each_line do |line|
39
+ if line == "# BREAK EVAL\n"
40
+ eval(chunked_ruby)
41
+ chunked_ruby = ''
42
+ else
43
+ chunked_ruby << line
44
+ end
45
+ end
46
+ eval(chunked_ruby) unless chunked_ruby == ''
47
+ end
48
+ end
49
+ end
50
+
51
+ def open(filename)
52
+ if filename[-3..-1] == '.gz'
53
+ Zlib::GzipReader.open(filename) do |file|
54
+ yield file
55
+ end
56
+ else
57
+ File.open(filename) do |file|
58
+ yield file
59
+ end
60
+ end
61
+ end
62
+
63
+ def filenames
64
+ filenames = []
65
+ @fixture_paths.each do |path|
66
+ filenames += (Dir[File.join(path, '*.rb')] + Dir[File.join(path, '*.rb.gz')]).sort
67
+ end
68
+ filenames.uniq!
69
+ filenames = filenames.find_all { |filename| filename =~ @filter } if @filter
70
+ filenames
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,107 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+
3
+ module SeedFu
4
+ # Creates or updates seed records with data.
5
+ #
6
+ # It is not recommended to use this class directly. Instead, use `Model.seed`, and `Model.seed_once`,
7
+ # where `Model` is your Active Record model.
8
+ #
9
+ # @see ActiveRecordExtension
10
+ class Seeder
11
+ # @param [ActiveRecord::Base] model_class The model to be seeded
12
+ # @param [Array<Symbol>] constraints A list of attributes which identify a particular seed. If
13
+ # a record with these attributes already exists then it will be updated rather than created.
14
+ # @param [Array<Hash>] data Each item in this array is a hash containing attributes for a
15
+ # particular record.
16
+ # @param [Hash] options
17
+ # @option options [Boolean] :quiet (SeedFu.quiet) If true, output will be silenced
18
+ # @option options [Boolean] :insert_only (false) If true then existing records which match the
19
+ # constraints will not be updated, even if the seed data has changed
20
+ def initialize(model_class, constraints, data, options = {})
21
+ @model_class = model_class
22
+ @constraints = constraints.to_a.empty? ? [:id] : constraints
23
+ @data = data.to_a || []
24
+ @options = options.symbolize_keys
25
+
26
+ @options[:quiet] ||= SeedFu.quiet
27
+
28
+ validate_constraints!
29
+ validate_data!
30
+ end
31
+
32
+ # Insert/update the records as appropriate. Validation is skipped while saving.
33
+ # @return [Array<ActiveRecord::Base>] The records which have been seeded
34
+ def seed
35
+ records = @model_class.transaction do
36
+ @data.map { |record_data| seed_record(record_data.symbolize_keys) }
37
+ end
38
+ update_id_sequence
39
+ records
40
+ end
41
+
42
+ private
43
+
44
+ def validate_constraints!
45
+ unknown_columns = @constraints.map(&:to_s) - @model_class.column_names
46
+ unless unknown_columns.empty?
47
+ raise(ArgumentError,
48
+ "Your seed constraints contained unknown columns: #{column_list(unknown_columns)}. " +
49
+ "Valid columns are: #{column_list(@model_class.column_names)}.")
50
+ end
51
+ end
52
+
53
+ def validate_data!
54
+ raise ArgumentError, "Seed data missing" if @data.empty?
55
+ end
56
+
57
+ def column_list(columns)
58
+ '`' + columns.join("`, `") + '`'
59
+ end
60
+
61
+ def seed_record(data)
62
+ record = find_or_initialize_record(data)
63
+ return if @options[:insert_only] && !record.new_record?
64
+
65
+ puts " - #{@model_class} #{data.inspect}" unless @options[:quiet]
66
+
67
+ # Rails 3 or Rails 4 + rails/protected_attributes
68
+ if record.class.respond_to?(:protected_attributes) && record.class.respond_to?(:accessible_attributes)
69
+ record.assign_attributes(data, :without_protection => true)
70
+ # Rails 4 without rails/protected_attributes
71
+ else
72
+ record.assign_attributes(data)
73
+ end
74
+ record.save(:validate => false) || raise(ActiveRecord::RecordNotSaved, 'Record not saved!')
75
+ record
76
+ end
77
+
78
+ def find_or_initialize_record(data)
79
+ @model_class.where(constraint_conditions(data)).take ||
80
+ @model_class.new
81
+ end
82
+
83
+ def constraint_conditions(data)
84
+ Hash[@constraints.map { |c| [c, data[c.to_sym]] }]
85
+ end
86
+
87
+ def update_id_sequence
88
+ if @model_class.connection.adapter_name == "PostgreSQL" or @model_class.connection.adapter_name == "PostGIS"
89
+ return if @model_class.primary_key.nil? || @model_class.sequence_name.nil?
90
+
91
+ max_seeded_id = @data.filter_map { |d| d["id"] }.max
92
+ seq = @model_class.connection.execute(<<~SQL)
93
+ SELECT last_value
94
+ FROM #{@model_class.sequence_name}
95
+ SQL
96
+ last_seq_value = seq.first["last_value"]
97
+
98
+ if max_seeded_id && last_seq_value < max_seeded_id
99
+ # Update the sequence to start from the highest existing id
100
+ @model_class.connection.reset_pk_sequence!(@model_class.table_name)
101
+ else
102
+ # The sequence is already higher than any of our seeded ids - better not touch it
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,4 @@
1
+ module SeedFu
2
+ # The current version of Seed Fu
3
+ VERSION = '2.3.12'
4
+ end
@@ -0,0 +1,132 @@
1
+ module SeedFu
2
+ # {Writer} is used to programmatically generated seed files. For example, you might want to write
3
+ # a script which converts data in a CSV file to a valid Seed Fu seed file, which can then be
4
+ # imported.
5
+ #
6
+ # @example Basic usage
7
+ # SeedFu::Writer.write('path/to/file.rb', :class_name => 'Person', :constraints => [:first_name, :last_name]) do |writer|
8
+ # writer.add(:first_name => 'Jon', :last_name => 'Smith', :age => 21)
9
+ # writer.add(:first_name => 'Emily', :last_name => 'McDonald', :age => 24)
10
+ # end
11
+ #
12
+ # # Writes the following to the file:
13
+ # #
14
+ # # Person.seed(:first_name, :last_name,
15
+ # # {:first_name=>"Jon", :last_name=>"Smith", :age=>21},
16
+ # # {:first_name=>"Emily", :last_name=>"McDonald", :age=>24}
17
+ # # )
18
+ class Writer
19
+ cattr_accessor :default_options
20
+ @@default_options = {
21
+ :chunk_size => 100,
22
+ :constraints => [:id],
23
+ :seed_type => :seed
24
+ }
25
+
26
+ # @param [Hash] options
27
+ # @option options [String] :class_name *Required* The name of the Active Record model to
28
+ # generate seeds for
29
+ # @option options [Fixnum] :chunk_size (100) The number of seeds to write before generating a
30
+ # `# BREAK EVAL` line. (Chunking reduces memory usage when loading seeds.)
31
+ # @option options [:seed, :seed_once] :seed_type (:seed) The method to use when generating
32
+ # seeds. See {ActiveRecordExtension} for details.
33
+ # @option options [Array<Symbol>] :constraints ([:id]) The constraining attributes for the seeds
34
+ def initialize(options = {})
35
+ @options = self.class.default_options.merge(options)
36
+ raise ArgumentError, "missing option :class_name" unless @options[:class_name]
37
+ end
38
+
39
+ # Creates a new instance of {Writer} with the `options`, and then calls {#write} with the
40
+ # `io_or_filename` and `block`
41
+ def self.write(io_or_filename, options = {}, &block)
42
+ new(options).write(io_or_filename, &block)
43
+ end
44
+
45
+ # Writes the necessary headers and footers, and yields to a block within which the actual
46
+ # seed data should be writting using the `#<<` method.
47
+ #
48
+ # @param [IO] io_or_filename The IO to which writes will be made. (If an `IO` is given, it is
49
+ # your responsibility to close it after writing.)
50
+ # @param [String] io_or_filename The filename of a file to make writes to. (Will be opened and
51
+ # closed automatically.)
52
+ # @yield [self] make calls to `#<<` within the block
53
+ def write(io_or_filename, &block)
54
+ raise ArgumentError, "missing block" unless block_given?
55
+
56
+ if io_or_filename.respond_to?(:write)
57
+ write_to_io(io_or_filename, &block)
58
+ else
59
+ File.open(io_or_filename, 'w') do |file|
60
+ write_to_io(file, &block)
61
+ end
62
+ end
63
+ end
64
+
65
+ # Add a seed. Must be called within a block passed to {#write}.
66
+ # @param [Hash] seed The attributes for the seed
67
+ def <<(seed)
68
+ raise "You must add seeds inside a SeedFu::Writer#write block" unless @io
69
+
70
+ buffer = ''
71
+
72
+ if chunk_this_seed?
73
+ buffer << seed_footer
74
+ buffer << "# BREAK EVAL\n"
75
+ buffer << seed_header
76
+ end
77
+
78
+ buffer << ",\n"
79
+ buffer << ' ' + seed.inspect
80
+
81
+ @io.write(buffer)
82
+
83
+ @count += 1
84
+ end
85
+ alias_method :add, :<<
86
+
87
+ private
88
+
89
+ def write_to_io(io)
90
+ @io, @count = io, 0
91
+ @io.write(file_header)
92
+ @io.write(seed_header)
93
+ yield(self)
94
+ @io.write(seed_footer)
95
+ @io.write(file_footer)
96
+ ensure
97
+ @io, @count = nil, nil
98
+ end
99
+
100
+ def file_header
101
+ <<-END
102
+ # DO NOT MODIFY THIS FILE, it was auto-generated.
103
+ #
104
+ # Date: #{Time.now}
105
+ # Seeding #{@options[:class_name]}
106
+ # Written with the command:
107
+ #
108
+ # #{$0} #{$*.join}
109
+ #
110
+ END
111
+ end
112
+
113
+ def file_footer
114
+ <<-END
115
+ # End auto-generated file.
116
+ END
117
+ end
118
+
119
+ def seed_header
120
+ constraints = @options[:constraints] && @options[:constraints].map(&:inspect).join(', ')
121
+ "#{@options[:class_name]}.#{@options[:seed_type]}(#{constraints}"
122
+ end
123
+
124
+ def seed_footer
125
+ "\n)\n"
126
+ end
127
+
128
+ def chunk_this_seed?
129
+ @count != 0 && (@count % @options[:chunk_size]) == 0
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,38 @@
1
+ require 'discourse-seed-fu'
2
+
3
+ namespace :db do
4
+ desc <<-EOS
5
+ Loads seed data for the current environment. It will look for
6
+ ruby seed files in <RAILS_ROOT>/db/fixtures/ and
7
+ <RAILS_ROOT>/db/fixtures/<RAILS_ENV>/.
8
+
9
+ By default it will load any ruby files found. You can filter the files
10
+ loaded by passing in the FILTER environment variable with a comma-delimited
11
+ list of patterns to include. Any files not matching the pattern will
12
+ not be loaded.
13
+
14
+ You can also change the directory where seed files are looked for
15
+ with the FIXTURE_PATH environment variable.
16
+
17
+ Examples:
18
+ # default, to load all seed files for the current environment
19
+ rake db:seed_fu
20
+
21
+ # to load seed files matching orders or customers
22
+ rake db:seed_fu FILTER=orders,customers
23
+
24
+ # to load files from RAILS_ROOT/features/fixtures
25
+ rake db:seed_fu FIXTURE_PATH=features/fixtures
26
+ EOS
27
+ task :seed_fu => :environment do
28
+ if ENV["FILTER"]
29
+ filter = /#{ENV["FILTER"].gsub(/,/, "|")}/
30
+ end
31
+
32
+ if ENV["FIXTURE_PATH"]
33
+ fixture_paths = [ENV["FIXTURE_PATH"], ENV["FIXTURE_PATH"] + '/' + Rails.env]
34
+ end
35
+
36
+ SeedFu.seed(fixture_paths, filter)
37
+ end
38
+ end
@@ -0,0 +1,12 @@
1
+ namespace :db do
2
+ desc 'Load seed data into database'
3
+ task :seed_fu do
4
+ on roles(:db) do
5
+ within release_path do
6
+ with rails_env: fetch(:rails_env) do
7
+ execute :bundle, :exec, :rake, 'db:seed_fu'
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: discourse-seed-fu
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.3.12
5
+ platform: ruby
6
+ authors:
7
+ - Michael Bleigh
8
+ - Jon Leighton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2022-10-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '3.1'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '3.1'
28
+ - !ruby/object:Gem::Dependency
29
+ name: activesupport
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '3.1'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '3.1'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '2.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '2.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: pg
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: mysql2
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: sqlite3
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ description: Seed Fu is an attempt to once and for all solve the problem of inserting
99
+ and maintaining seed data in a database. It uses a variety of techniques gathered
100
+ from various places around the web and combines them to create what is hopefully
101
+ the most robust seed data system around.
102
+ email:
103
+ - michael@intridea.com
104
+ - j@jonathanleighton.com
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - CHANGELOG.md
110
+ - LICENSE
111
+ - README.md
112
+ - lib/discourse-seed-fu.rb
113
+ - lib/seed-fu/active_record_extension.rb
114
+ - lib/seed-fu/block_hash.rb
115
+ - lib/seed-fu/capistrano.rb
116
+ - lib/seed-fu/capistrano3.rb
117
+ - lib/seed-fu/railtie.rb
118
+ - lib/seed-fu/runner.rb
119
+ - lib/seed-fu/seeder.rb
120
+ - lib/seed-fu/version.rb
121
+ - lib/seed-fu/writer.rb
122
+ - lib/tasks/seed_fu.rake
123
+ - lib/tasks/seed_fu_capistrano3.rake
124
+ homepage: http://github.com/mbleigh/seed-fu
125
+ licenses:
126
+ - MIT
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubygems_version: 3.1.6
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Easily manage seed data in your Active Record application
147
+ test_files: []