praxis 2.0.pre.5 → 2.0.pre.6

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.
Files changed (76) hide show
  1. checksums.yaml +5 -5
  2. data/.rspec +0 -1
  3. data/.ruby-version +1 -0
  4. data/CHANGELOG.md +22 -0
  5. data/Gemfile +1 -1
  6. data/Guardfile +2 -1
  7. data/Rakefile +1 -7
  8. data/TODO.md +28 -0
  9. data/lib/api_browser/package-lock.json +7110 -0
  10. data/lib/praxis.rb +6 -4
  11. data/lib/praxis/action_definition.rb +9 -16
  12. data/lib/praxis/application.rb +1 -2
  13. data/lib/praxis/bootloader_stages/routing.rb +2 -4
  14. data/lib/praxis/extensions/attribute_filtering.rb +2 -0
  15. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +148 -157
  16. data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +15 -0
  17. data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +90 -0
  18. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +68 -0
  19. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +58 -0
  20. data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +35 -0
  21. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +9 -12
  22. data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +3 -2
  23. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +7 -9
  24. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +6 -9
  25. data/lib/praxis/extensions/pagination.rb +130 -0
  26. data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +42 -0
  27. data/lib/praxis/extensions/pagination/header_generator.rb +70 -0
  28. data/lib/praxis/extensions/pagination/ordering_params.rb +234 -0
  29. data/lib/praxis/extensions/pagination/pagination_handler.rb +68 -0
  30. data/lib/praxis/extensions/pagination/pagination_params.rb +374 -0
  31. data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +45 -0
  32. data/lib/praxis/handlers/json.rb +2 -0
  33. data/lib/praxis/handlers/www_form.rb +5 -0
  34. data/lib/praxis/handlers/{xml.rb → xml-sample.rb} +6 -0
  35. data/lib/praxis/mapper/active_model_compat.rb +23 -5
  36. data/lib/praxis/mapper/resource.rb +16 -9
  37. data/lib/praxis/mapper/sequel_compat.rb +1 -0
  38. data/lib/praxis/media_type.rb +1 -56
  39. data/lib/praxis/plugins/mapper_plugin.rb +1 -1
  40. data/lib/praxis/plugins/pagination_plugin.rb +71 -0
  41. data/lib/praxis/resource_definition.rb +4 -12
  42. data/lib/praxis/route.rb +2 -4
  43. data/lib/praxis/routing_config.rb +4 -8
  44. data/lib/praxis/tasks/routes.rb +9 -14
  45. data/lib/praxis/validation_handler.rb +1 -2
  46. data/lib/praxis/version.rb +1 -1
  47. data/praxis.gemspec +2 -3
  48. data/spec/functional_spec.rb +9 -6
  49. data/spec/praxis/action_definition_spec.rb +4 -16
  50. data/spec/praxis/api_general_info_spec.rb +6 -6
  51. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +304 -0
  52. data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +39 -0
  53. data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +34 -0
  54. data/spec/praxis/extensions/field_expansion_spec.rb +6 -24
  55. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +15 -11
  56. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +4 -3
  57. data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +130 -0
  58. data/spec/praxis/extensions/{field_selection/support → support}/spec_resources_active_model.rb +45 -2
  59. data/spec/praxis/extensions/{field_selection/support → support}/spec_resources_sequel.rb +0 -0
  60. data/spec/praxis/media_type_spec.rb +5 -129
  61. data/spec/praxis/request_spec.rb +3 -22
  62. data/spec/praxis/resource_definition_spec.rb +1 -1
  63. data/spec/praxis/response_definition_spec.rb +1 -5
  64. data/spec/praxis/route_spec.rb +2 -9
  65. data/spec/praxis/routing_config_spec.rb +4 -13
  66. data/spec/praxis/types/multipart_array_spec.rb +4 -21
  67. data/spec/spec_app/config/environment.rb +0 -2
  68. data/spec/spec_app/design/api.rb +1 -1
  69. data/spec/spec_app/design/media_types/instance.rb +0 -8
  70. data/spec/spec_app/design/media_types/volume.rb +0 -12
  71. data/spec/spec_app/design/resources/instances.rb +1 -2
  72. data/spec/spec_helper.rb +6 -0
  73. data/spec/support/spec_media_types.rb +0 -73
  74. metadata +35 -45
  75. data/spec/praxis/handlers/xml_spec.rb +0 -177
  76. data/spec/praxis/links_spec.rb +0 -68
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ require 'praxis/extensions/attribute_filtering'
4
+
5
+ describe Praxis::Extensions::AttributeFiltering::FilterTreeNode do
6
+
7
+ let(:filters) do
8
+ [
9
+ {name: 'one', specs: { op: '>', value: 1}},
10
+ {name: 'one', specs: { op: '<', value: 10}},
11
+ {name: 'rel1.a1', specs: { op: '=', value: 1}},
12
+ {name: 'rel1.a2', specs: { op: '=', value: 2}},
13
+ {name: 'rel1.rel2.b1', specs: { op: '=', value: 11}},
14
+ {name: 'rel1.rel2.b2', specs: { op: '=', value: 12}}
15
+ ]
16
+ end
17
+ context 'initialization' do
18
+ subject { described_class.new(filters) }
19
+ it 'holds the top conditions and the child in a TreeNode' do
20
+ expect(subject.path).to eq([])
21
+ expect(subject.conditions.size).to eq(2)
22
+ expect(subject.children.keys).to eq(['rel1'])
23
+ expect(subject.children['rel1']).to be_kind_of(described_class)
24
+ end
25
+
26
+ it 'recursively holds the conditions and the children of their children in a TreeNode' do
27
+ rel1 = subject.children['rel1']
28
+ expect(rel1.path).to eq(['rel1'])
29
+ expect(rel1.conditions.size).to eq(2)
30
+ expect(rel1.children.keys).to eq(['rel2'])
31
+ expect(rel1.children['rel2']).to be_kind_of(described_class)
32
+
33
+ rel1rel2 = rel1.children['rel2']
34
+ expect(rel1rel2.path).to eq(['rel1','rel2'])
35
+ expect(rel1rel2.conditions.size).to eq(2)
36
+ expect(rel1rel2.children.keys).to be_empty
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ require 'praxis/extensions/attribute_filtering'
4
+
5
+ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
6
+
7
+ context '.load' do
8
+ subject { described_class.load(filters_string) }
9
+ context 'parses for operator' do
10
+ described_class::AVAILABLE_OPERATORS.each do |op|
11
+ it "#{op}" do
12
+ str = "thename#{op}thevalue"
13
+ parsed = [{ name: :thename, op: op, value: 'thevalue'}]
14
+ expect(described_class.load(str).parsed_array).to eq(parsed)
15
+ end
16
+ end
17
+ end
18
+ context 'with all operators at once' do
19
+ let(:filters_string) { 'one=1&two!=2&three>=3&four<=4&five<5&six>6&seven!&eight!!'}
20
+ it do
21
+ expect(subject.parsed_array).to eq([
22
+ { name: :one, op: '=', value: '1'},
23
+ { name: :two, op: '!=', value: '2'},
24
+ { name: :three, op: '>=', value: '3'},
25
+ { name: :four, op: '<=', value: '4'},
26
+ { name: :five, op: '<', value: '5'},
27
+ { name: :six, op: '>', value: '6'},
28
+ { name: :seven, op: '!', value: nil},
29
+ { name: :eight, op: '!!', value: nil},
30
+ ])
31
+ end
32
+ end
33
+ end
34
+ end
@@ -47,15 +47,10 @@ describe Praxis::Extensions::FieldExpansion do
47
47
 
48
48
  context 'and no fields provided' do
49
49
  it 'returns the fields for the default view' do
50
- expect(expansion).to eq({id: true, name: true, links: [true]})
50
+ expect(expansion).to eq({id: true, name: true})
51
51
  end
52
52
 
53
- context 'and a view' do
54
- let(:view) { :link }
55
- it 'expands the fields on the view' do
56
- expect(expansion).to eq({id: true, name: true, href: true})
57
- end
58
- end
53
+ pending 'and a view'
59
54
  end
60
55
 
61
56
  context 'with a set of fields provided' do
@@ -65,15 +60,7 @@ describe Praxis::Extensions::FieldExpansion do
65
60
  expect(expansion).to eq expected
66
61
  end
67
62
 
68
- context 'and a view' do
69
- let(:view) { :link }
70
- let(:fields) { 'id,href' }
71
-
72
- it 'returns the subset of fields that exist for the view' do
73
- expected = {id: true, href: true }
74
- expect(expansion).to eq expected
75
- end
76
- end
63
+ pending 'and a view'
77
64
  end
78
65
  end
79
66
 
@@ -81,15 +68,10 @@ describe Praxis::Extensions::FieldExpansion do
81
68
  let(:test_attributes) { {view: true} }
82
69
 
83
70
  it 'returns the fields for the default view' do
84
- expect(expansion).to eq({id: true, name: true, links: [true]})
71
+ expect(expansion).to eq({id: true, name: true})
85
72
  end
86
73
 
87
- context 'and a view' do
88
- let(:view) { :link }
89
- it 'expands the fields on the view' do
90
- expect(expansion).to eq({id: true, name: true, href: true})
91
- end
92
- end
74
+ pending 'and a view'
93
75
  end
94
76
 
95
77
 
@@ -98,7 +80,7 @@ describe Praxis::Extensions::FieldExpansion do
98
80
  let(:fields){ nil }
99
81
  let(:view){ nil }
100
82
  it 'ignores incoming parameters and expands for the default view' do
101
- expect(expansion).to eq({id: true, name: true, links: [true]})
83
+ expect(expansion).to eq({id: true, name: true})
102
84
  end
103
85
  end
104
86
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- require_relative 'support/spec_resources_active_model.rb'
3
+ require_relative '../support/spec_resources_active_model.rb'
4
4
 
5
5
  describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
6
6
  let(:selector_fields) do
@@ -30,8 +30,9 @@ describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
30
30
  ]
31
31
  end
32
32
  let(:selector_node) { Praxis::Mapper::SelectorGenerator.new.add(ActiveBookResource,selector_fields) }
33
+ let(:debug){ false }
33
34
 
34
- subject(:selector) {described_class.new(query: query, selectors: selector_node) }
35
+ subject(:selector) {described_class.new(query: query, selectors: selector_node, debug: debug) }
35
36
  context '#generate with a mocked' do
36
37
  let(:query) { double("Query") }
37
38
  it 'calls the select columns for the top level, and includes the right association hashes' do
@@ -49,12 +50,15 @@ describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
49
50
  expect(subject).to_not receive(:explain_query)
50
51
  subject.generate
51
52
  end
52
- it 'calls the explain debug method if enabled' do
53
- expect(query).to receive(:select).and_return(query)
54
- expect(query).to receive(:includes).and_return(query)
55
- expect(subject).to receive(:explain_query)
56
- subject.generate(debug: true)
57
- end
53
+ context 'when debug is enabled' do
54
+ let(:debug){ true }
55
+ it 'calls the explain method' do
56
+ expect(query).to receive(:select).and_return(query)
57
+ expect(query).to receive(:includes).and_return(query)
58
+ expect(subject).to receive(:explain_query)
59
+ subject.generate
60
+ end
61
+ end
58
62
  end
59
63
 
60
64
  context '#generate with a real AR model' do
@@ -79,14 +83,14 @@ describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
79
83
  expect(includes_hash).to match(expected_includes)
80
84
  # Also, make AR do the actual query to make sure everything is wired up correctly
81
85
  result = final_query.to_a
82
- expect(result.size).to be 2
86
+ expect(result.size).to be > 2 # We are using 2 but we've seeded more
83
87
  book1 = result[0]
84
88
  book2 = result[1]
85
89
  expect(book1.author.id).to eq 11
86
90
  expect(book1.author.books.size).to eq 1
87
91
  expect(book1.author.books.map(&:simple_name)).to eq(['Book1'])
88
92
  expect(book1.category.name).to eq 'cat1'
89
- expect(book1.tags.map(&:name)).to match_array(['blue','red'])
93
+ expect(book1.tags.map(&:name)).to match_array(['blue','red','green'])
90
94
 
91
95
  expect(book2.author.id).to eq 22
92
96
  expect(book2.author.books.size).to eq 1
@@ -98,7 +102,7 @@ describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
98
102
  it 'calls the explain debug method if enabled' do
99
103
  suppress_output do
100
104
  # Actually make it run all the way...but suppressing the output
101
- subject.generate(debug: true)
105
+ subject.generate
102
106
  end
103
107
  end
104
108
  end
@@ -37,7 +37,7 @@ describe Praxis::Extensions::FieldSelection::SequelQuerySelector do
37
37
  # Pay the price for creating and connecting only in this spec instead in spec helper
38
38
  # this way all other specs do not need to be slower and it's a better TDD experience
39
39
 
40
- require_relative 'support/spec_resources_sequel.rb'
40
+ require_relative '../support/spec_resources_sequel.rb'
41
41
 
42
42
  let(:selector_fields) do
43
43
  {
@@ -65,9 +65,10 @@ describe Praxis::Extensions::FieldSelection::SequelQuerySelector do
65
65
  end
66
66
 
67
67
  let(:selector_node) { Praxis::Mapper::SelectorGenerator.new.add(SequelSimpleResource,selector_fields) }
68
- subject {described_class.new(query: query, selectors: selector_node) }
68
+ subject {described_class.new(query: query, selectors: selector_node, debug: debug) }
69
69
 
70
70
  context 'generate' do
71
+ let(:debug) { false }
71
72
  context 'using the real models and DB' do
72
73
  let(:query) { SequelSimpleModel }
73
74
 
@@ -109,7 +110,7 @@ describe Praxis::Extensions::FieldSelection::SequelQuerySelector do
109
110
  it 'calls the explain debug method if enabled' do
110
111
  suppress_output do
111
112
  # Actually make it run all the way...but suppressing the output
112
- subject.generate(debug: true)
113
+ subject.generate
113
114
  end
114
115
  end
115
116
  end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ require_relative '../support/spec_resources_active_model.rb'
4
+ require 'praxis/extensions/pagination'
5
+
6
+ class Book < Praxis::MediaType
7
+ attributes do
8
+ attribute :id, Integer
9
+ attribute :simple_name, String
10
+ attribute :category_uuid, String
11
+ end
12
+ end
13
+ Book.finalize!
14
+ BookPaginationParamsAttribute = Attributor::Attribute.new(Praxis::Types::PaginationParams.for(Book)) do
15
+ max_items 3
16
+ page_size 2
17
+ # disallow :paging
18
+ default by: :id
19
+ end
20
+
21
+ BookOrderingParamsAttribute = Attributor::Attribute.new(Praxis::Types::OrderingParams.for(Book)) do
22
+ enforce_for :all
23
+ end
24
+
25
+ describe Praxis::Extensions::Pagination::ActiveRecordPaginationHandler do
26
+ shared_examples 'sorts_the_same' do |op, expected|
27
+ let(:order_params) { BookOrderingParamsAttribute.load(op) }
28
+ it do
29
+ loaded_ids = subject.all.map(&:id)
30
+ expected_ids = expected.all.map(&:id)
31
+ expect(loaded_ids).to eq(expected_ids)
32
+ end
33
+ end
34
+
35
+ shared_examples 'paginates_the_same' do |par, expected|
36
+ let(:paginator_params) { BookPaginationParamsAttribute.load(par) }
37
+ it do
38
+ loaded_ids = subject.all.map(&:id)
39
+ expected_ids = expected.all.map(&:id)
40
+ expect(loaded_ids).to eq(expected_ids)
41
+ end
42
+ end
43
+
44
+ let(:query) { ActiveBook }
45
+ let(:table) { ActiveBook.table_name }
46
+ let(:paginator_params) { nil }
47
+ let(:order_params) { nil }
48
+ let(:pagination) do
49
+ Praxis::Extensions::Pagination::PaginationStruct.new(paginator_params, order_params)
50
+ end
51
+
52
+
53
+ context '.paginate' do
54
+ subject {described_class.paginate(query, pagination) }
55
+
56
+ context 'empty struct' do
57
+ let(:paginator_params) { nil }
58
+
59
+ it 'does not change the query with an empty struct' do
60
+ expect(subject).to be(query)
61
+ end
62
+ end
63
+
64
+ context 'page-based' do
65
+ it_behaves_like 'paginates_the_same', 'page=1,items=3',
66
+ ::ActiveBook.limit(3)
67
+ it_behaves_like 'paginates_the_same', 'page=2,items=3',
68
+ ::ActiveBook.offset(3).limit(3)
69
+ end
70
+
71
+ context 'page-based with defaults' do
72
+ it_behaves_like 'paginates_the_same', '',
73
+ ::ActiveBook.offset(0).limit(2)
74
+ it_behaves_like 'paginates_the_same', 'page=2',
75
+ ::ActiveBook.offset(2).limit(2)
76
+ end
77
+
78
+ context 'cursor-based' do
79
+ it_behaves_like 'paginates_the_same', 'by=id,items=3',
80
+ ::ActiveBook.limit(3).order(id: :asc)
81
+ it_behaves_like 'paginates_the_same', 'by=id,from=1000,items=3',
82
+ ::ActiveBook.where("id > 1000").limit(3).order(id: :asc)
83
+ it_behaves_like 'paginates_the_same', 'by=simple_name,from=Book1000,items=3',
84
+ ::ActiveBook.where("simple_name > 'Book1000'").limit(3).order(simple_name: :asc)
85
+ end
86
+
87
+ context 'cursor-based with defaults' do
88
+ it_behaves_like 'paginates_the_same', '',
89
+ ::ActiveBook.limit(2).order(id: :asc)
90
+ it_behaves_like 'paginates_the_same', 'by=id,from=1000',
91
+ ::ActiveBook.where("id > 1000").limit(2).order(id: :asc)
92
+ end
93
+
94
+ context 'including order' do
95
+ let(:order_params) { BookOrderingParamsAttribute.load(op_string) }
96
+
97
+ context 'when compatible with cursor' do
98
+ let(:op_string){ 'id'}
99
+ # Compatible cursor field
100
+ it_behaves_like 'paginates_the_same', 'by=id,items=3',
101
+ ::ActiveBook.limit(3).order(id: :asc)
102
+ end
103
+
104
+ context 'when incompatible with cursor' do
105
+ let(:op_string){ 'id'}
106
+ let(:paginator_params) { BookPaginationParamsAttribute.load('by=simple_name,items=3') }
107
+ it do
108
+ expect{subject.all}.to raise_error(described_class::PaginationException, /is incompatible with pagination/)
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ context '.order' do
115
+ subject {described_class.order(query, pagination.order) }
116
+
117
+ it 'does not change the query with an empty struct' do
118
+ expect(subject).to be(query)
119
+ end
120
+
121
+ it_behaves_like 'sorts_the_same', 'simple_name',
122
+ ::ActiveBook.order(simple_name: :asc)
123
+ it_behaves_like 'sorts_the_same', '-simple_name',
124
+ ::ActiveBook.order(simple_name: :desc)
125
+ it_behaves_like 'sorts_the_same', '-simple_name,id',
126
+ ::ActiveBook.order(simple_name: :desc, id: :asc)
127
+ it_behaves_like 'sorts_the_same', '-simple_name,-id',
128
+ ::ActiveBook.order(simple_name: :desc, id: :desc)
129
+ end
130
+ end
@@ -36,6 +36,7 @@ def create_tables
36
36
  create_table :active_taggings do |table|
37
37
  table.column :book_id, :integer
38
38
  table.column :tag_id, :integer
39
+ table.column :label, :string, null: true
39
40
  end
40
41
  end
41
42
  end
@@ -48,8 +49,12 @@ class ActiveBook < ActiveRecord::Base
48
49
 
49
50
  belongs_to :category, class_name: 'ActiveCategory', foreign_key: :category_uuid, primary_key: :uuid
50
51
  belongs_to :author, class_name: 'ActiveAuthor'
52
+
51
53
  has_many :taggings, class_name: 'ActiveTagging', foreign_key: :book_id
54
+ has_many :primary_taggings, lambda { where(label: 'primary')}, class_name: 'ActiveTagging', foreign_key: :book_id
55
+
52
56
  has_many :tags, class_name: 'ActiveTag', through: :taggings
57
+ has_many :primary_tags, class_name: 'ActiveTag', through: :primary_taggings, source: :tag
53
58
  end
54
59
 
55
60
  class ActiveAuthor < ActiveRecord::Base
@@ -64,6 +69,8 @@ end
64
69
 
65
70
  class ActiveTag < ActiveRecord::Base
66
71
  include Praxis::Mapper::ActiveModelCompat
72
+ has_many :taggings, class_name: 'ActiveTagging', foreign_key: :tag_id
73
+ has_many :books, class_name: 'ActiveBook', through: :taggings, source: :book
67
74
  end
68
75
 
69
76
  class ActiveTagging < ActiveRecord::Base
@@ -94,6 +101,26 @@ end
94
101
  class ActiveBookResource < ActiveBaseResource
95
102
  model ActiveBook
96
103
 
104
+ filters_mapping(
105
+ id: :id,
106
+ category_uuid: :category_uuid,
107
+ 'fake_nested.name': 'simple_name',
108
+ 'name': 'simple_name',
109
+ 'name_is_not': lambda do |spec| # Silly way to use a proc, but good enough for testing
110
+ { name: :simple_name, value: spec[:value] , op: '!=' } # Can be an array for multiple conditions as well
111
+ end,
112
+ 'author.id': 'author.id',
113
+ 'author.name': 'author.name',
114
+ 'taggings.label': 'taggings.label',
115
+ 'taggings.tag_id': 'taggings.tag_id',
116
+ 'taggings.tag.taggings.tag_id': 'taggings.tag.taggings.tag_id',
117
+ 'tags.name': 'tags.name',
118
+ 'primary_tags.name': 'primary_tags.name',
119
+ 'category.name': 'category.name',
120
+ 'category.books.name': 'category.books.simple_name',
121
+ 'category.books.taggings.tag_id': 'category.books.taggings.tag_id',
122
+ 'category.books.taggings.label': 'category.books.taggings.label',
123
+ )
97
124
  # Forces to add an extra column (added_column)...and yet another (author_id) that will serve
98
125
  # to check that if that's already automatically added due to an association, it won't interfere or duplicate
99
126
  property :author, dependencies: [:author, :added_column, :author_id]
@@ -108,23 +135,39 @@ def seed_data
108
135
 
109
136
  author1 = ActiveAuthor.create( id: 11, name: 'author1' )
110
137
  author2 = ActiveAuthor.create( id: 22, name: 'author2' )
138
+ author3 = ActiveAuthor.create( id: 33, name: nil )
111
139
 
112
140
  tag_blue = ActiveTag.create(id: 1 , name: 'blue' )
113
141
  tag_red = ActiveTag.create(id: 2 , name: 'red' )
142
+ tag_green = ActiveTag.create(id: 3 , name: 'green' )
114
143
 
115
144
  book1 = ActiveBook.create( id: 1 , simple_name: 'Book1', category_uuid: 'deadbeef1')
116
145
  book1.author = author1
117
146
  book1.category = cat1
118
147
  book1.save
119
- ActiveTagging.create(book: book1, tag: tag_blue)
148
+ ActiveTagging.create(book: book1, tag: tag_blue, label: 'primary')
120
149
  ActiveTagging.create(book: book1, tag: tag_red)
150
+ ActiveTagging.create(book: book1, tag: tag_green, label: 'primary')
121
151
 
122
152
 
123
153
  book2 = ActiveBook.create( id: 2 , simple_name: 'Book2', category_uuid: 'deadbeef1')
124
154
  book2.author = author2
125
155
  book2.category = cat2
126
156
  book2.save
127
- ActiveTagging.create(book: book2, tag: tag_red)
157
+ ActiveTagging.create(book: book2, tag: tag_red, label: 'primary')
158
+
159
+ book3 = ActiveBook.create( id: 3 , simple_name: 'Book3', category_uuid: 'deadbeef1')
160
+ book3.author = author3
161
+ book3.save
162
+ ActiveTagging.create(book: book3, tag: tag_red, label: 'primary')
163
+
164
+ # More stuff
165
+
166
+ 10.times do |i|
167
+ bid = 1000+i
168
+ ActiveBook.create( id: bid , simple_name: "Book#{bid}")
169
+ end
170
+
128
171
  end
129
172
 
130
173
  seed_data