hippo-fw 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +16 -11
  4. data/Rakefile +0 -7
  5. data/bin/hippo +5 -1
  6. data/client/hippo/__mocks__/config.js +2 -3
  7. data/client/hippo/boot.jsx +0 -2
  8. data/client/hippo/components/asset.jsx +1 -1
  9. data/client/hippo/components/form.jsx +8 -4
  10. data/client/hippo/components/form/fields.jsx +28 -14
  11. data/client/hippo/components/form/model.js +65 -20
  12. data/client/hippo/components/form/wrapper.jsx +11 -5
  13. data/client/hippo/components/icon.jsx +1 -1
  14. data/client/hippo/components/master-detail.jsx +66 -0
  15. data/client/hippo/components/master-detail.scss +50 -0
  16. data/client/hippo/components/record-finder.jsx +5 -5
  17. data/client/hippo/components/tool-tip.jsx +20 -0
  18. data/client/hippo/config.js +5 -3
  19. data/client/hippo/extensions/base.js +4 -0
  20. data/client/hippo/lib/smooth-scroll.js +17 -16
  21. data/client/hippo/models/asset.js +8 -10
  22. data/client/hippo/models/collection.js +1 -4
  23. data/client/hippo/models/query/array-result.js +11 -9
  24. data/client/hippo/models/sync.js +3 -3
  25. data/client/hippo/models/tenant.js +29 -0
  26. data/client/hippo/screens/system-settings.jsx +5 -4
  27. data/client/hippo/screens/system-settings/mailer-config.jsx +11 -17
  28. data/client/hippo/screens/system-settings/tenant.jsx +90 -0
  29. data/client/hippo/screens/user-management/edit-form.jsx +15 -25
  30. data/client/hippo/testing/index.js +1 -0
  31. data/client/hippo/workspace/styles.scss +0 -23
  32. data/command-reference-files/initial/.babelrc +10 -8
  33. data/command-reference-files/initial/Gemfile +1 -1
  34. data/{views/index.html → command-reference-files/initial/views/index.erb} +1 -0
  35. data/config/routes.rb +48 -17
  36. data/config/webpack.config.js +7 -12
  37. data/db/migrate/01_create_tenants.rb +13 -0
  38. data/db/migrate/{01_create_system_settings.rb → 02_create_system_settings.rb} +2 -1
  39. data/db/migrate/{02_create_assets.rb → 03_create_assets.rb} +2 -4
  40. data/db/migrate/{20140615031600_create_users.rb → 04_create_users.rb} +4 -2
  41. data/db/seed.rb +10 -1
  42. data/hippo-fw.gemspec +53 -51
  43. data/lib/hippo.rb +7 -1
  44. data/lib/hippo/access.rb +0 -1
  45. data/lib/hippo/access/roles/basic_user.rb +2 -0
  46. data/lib/hippo/api.rb +4 -3
  47. data/lib/hippo/{access → api}/authentication_provider.rb +3 -1
  48. data/lib/hippo/api/controller_base.rb +2 -2
  49. data/lib/hippo/api/handlers/asset.rb +28 -2
  50. data/lib/hippo/api/handlers/tenant.rb +26 -0
  51. data/lib/hippo/api/helper_methods.rb +5 -13
  52. data/lib/hippo/api/request_wrapper.rb +8 -1
  53. data/lib/hippo/api/root.rb +50 -51
  54. data/lib/hippo/api/route_set.rb +101 -0
  55. data/lib/hippo/api/routing.rb +9 -98
  56. data/lib/hippo/api/tenant_domain_router.rb +21 -0
  57. data/lib/hippo/asset.rb +1 -0
  58. data/lib/hippo/command.rb +1 -24
  59. data/lib/hippo/command/app.rb +4 -3
  60. data/lib/hippo/command/console.rb +7 -0
  61. data/lib/hippo/command/generate.rb +0 -5
  62. data/lib/hippo/command/guard.rb +1 -0
  63. data/lib/hippo/command/jest.rb +2 -2
  64. data/lib/hippo/command/server.rb +1 -3
  65. data/lib/hippo/command/webpack.rb +6 -26
  66. data/lib/hippo/configuration.rb +21 -13
  67. data/lib/hippo/db.rb +2 -0
  68. data/lib/hippo/db/migrations.rb +9 -2
  69. data/lib/hippo/extension.rb +49 -14
  70. data/lib/hippo/extension/definition.rb +0 -4
  71. data/lib/hippo/guard_tasks.rb +3 -11
  72. data/lib/hippo/mailer.rb +28 -16
  73. data/lib/hippo/model.rb +10 -0
  74. data/lib/hippo/numbers.rb +1 -1
  75. data/lib/hippo/rake_tasks.rb +7 -1
  76. data/lib/hippo/spec_helper.rb +33 -11
  77. data/lib/hippo/system_settings.rb +1 -0
  78. data/lib/hippo/templates/base.rb +1 -1
  79. data/lib/hippo/templates/mail.rb +26 -0
  80. data/lib/hippo/templates/tenant_change.rb +23 -0
  81. data/lib/hippo/tenant.rb +53 -0
  82. data/lib/hippo/user.rb +12 -6
  83. data/lib/hippo/version.rb +1 -1
  84. data/lib/hippo/webpack.rb +57 -0
  85. data/lib/hippo/{command → webpack}/client_config.rb +7 -21
  86. data/package.json +3 -3
  87. data/spec/client/components/__snapshots__/master-detail.spec.jsx.snap +22 -0
  88. data/spec/client/components/form.spec.jsx +14 -14
  89. data/spec/client/components/master-detail.spec.jsx +24 -0
  90. data/spec/client/components/record-finder.spec.jsx +5 -2
  91. data/spec/client/models/asset.spec.js +2 -13
  92. data/spec/client/models/base.spec.js +1 -11
  93. data/spec/client/models/query.spec.js +2 -4
  94. data/spec/client/models/sync.spec.js +7 -0
  95. data/spec/client/screens/__snapshots__/system-settings.spec.jsx.snap +79 -0
  96. data/spec/client/screens/system-settings-tenants.spec.jsx +18 -0
  97. data/spec/client/workspace/__snapshots__/menu.spec.jsx.snap +29 -313
  98. data/spec/client/workspace/menu.spec.jsx +1 -9
  99. data/spec/factories/tenant.rb +13 -0
  100. data/spec/fixtures/mail/test_email.liquid +1 -0
  101. data/spec/fixtures/{test_printer.tex → test_printer.tex.erb} +0 -0
  102. data/spec/server/api/controller_base_spec.rb +1 -1
  103. data/spec/server/api/tenant_change_spec.rb +24 -0
  104. data/spec/server/api/tenant_isolation_spec.rb +37 -0
  105. data/spec/server/asset_spec.rb +6 -6
  106. data/spec/server/command_spec.rb +0 -5
  107. data/spec/server/mailer_spec.rb +25 -23
  108. data/spec/server/numbers_spec.rb +12 -13
  109. data/spec/server/print/form_spec.rb +2 -1
  110. data/spec/server/strings_spec.rb +13 -13
  111. data/templates/.babelrc +10 -8
  112. data/templates/js/screen-definitions.js +8 -10
  113. data/templates/mail/tenant_change.liquid +13 -0
  114. data/{command-reference-files/initial/views/index.html → views/index.erb} +5 -2
  115. data/yarn.lock +22 -169
  116. metadata +56 -30
  117. data/client/hippo/components/form/field-prop-type.js +0 -16
  118. data/lib/hippo/api/default_routes.rb +0 -38
  119. data/lib/hippo/command/generate_component.rb +0 -28
  120. data/lib/hippo/command/generate_component.usage +0 -11
  121. data/lib/hippo/command/webpack_view.rb +0 -32
  122. data/lib/hippo/multi_server_boot.rb +0 -26
  123. data/lib/hippo/reloadable_view.rb +0 -13
  124. data/templates/client/components/.gitkeep +0 -0
  125. data/templates/client/components/BaseComponent.coffee +0 -9
  126. data/templates/client/components/Component.cjsx +0 -4
  127. data/templates/client/components/template.html +0 -3
@@ -3,26 +3,38 @@ require 'mail'
3
3
  module Hippo
4
4
 
5
5
  module Mailer
6
+ class << self
6
7
 
7
- def self.create(args = {})
8
- config = SystemSettings.for_ext(:hippo)['smtp'] || {}
9
- delivery = delivery_method_config(config)
10
- Mail::Message.new(args) do
11
- from "\"#{config['from_name']}\" <#{config['from_email']}>"
12
- delivery_method delivery
8
+ def create
9
+ config = SystemSettings.for_ext(:smtp)
10
+ delivery = delivery_method_config(config)
11
+ Mail::Message.new do
12
+ from "\"#{config['from_name']}\" <#{config['from_email']}>"
13
+ delivery_method delivery[:via], delivery[:config]
14
+ end
13
15
  end
14
- end
15
16
 
16
- def self.delivery_method_config(config)
17
- return :test unless Hippo.env.production?
18
- {
19
- smtp: {
20
- address: config['server'],
21
- user_name: config['login'],
22
- password: config['password']
17
+ def delivery_method_config(config)
18
+ {
19
+ via: Hippo.env.production? ? :smtp : :test,
20
+ config: {
21
+ address: config['address'],
22
+ user_name: config['user_name'],
23
+ password: config['password'],
24
+ enable_starttls_auto: true,
25
+ port: 587
26
+ }
23
27
  }
24
- }
25
- end
28
+ end
26
29
 
30
+ def from_template(template)
31
+ mail = create
32
+ mail.content_type = 'text/html; charset=UTF-8'
33
+ mail.body = template.render
34
+ mail.to = template.to
35
+ mail.subject = template.subject
36
+ mail
37
+ end
38
+ end
27
39
  end
28
40
  end
@@ -1,4 +1,5 @@
1
1
  require_relative 'concerns/all'
2
+ require 'activerecord-multi-tenant'
2
3
 
3
4
  module Hippo
4
5
 
@@ -19,6 +20,15 @@ module Hippo
19
20
  include Concerns::SortingExpressions
20
21
  include Concerns::RandomIdentifer
21
22
 
23
+ def self.belongs_to_tenant
24
+ belongs_to :tenant, class_name: 'Hippo::Tenant'
25
+ multi_tenant :tenant
26
+ end
27
+
28
+ # will be overridden if models call belongs_to_tenant
29
+ def self.scoped_by_tenant?
30
+ false
31
+ end
22
32
  end
23
33
 
24
34
  end
@@ -31,7 +31,7 @@ module Hippo
31
31
  # @param perc_or_num [BigDecimal,String, Integer] any value that BigDecimal will accept
32
32
  def initialize( perc_or_num )
33
33
  @is_perc = !! perc_or_num.to_s.match( /\%\s*$/ )
34
- @right_side = BigDecimal.new( perc_or_num, 5 )
34
+ @right_side = BigDecimal.new( perc_or_num.gsub(/[^0-9\.\-]/, ''), 5 )
35
35
  if is_percentage?
36
36
  @right_side *= 0.01
37
37
  end
@@ -62,8 +62,14 @@ task :test => [:spec] do
62
62
  ::Hippo::Command::Jest.new.configure.single_run
63
63
  end
64
64
 
65
- task :ci => ['db:migrate', 'db:seed'] do
65
+ task :lint do
66
66
  sh %{node node_modules/eslint/bin/eslint.js client spec/client/}
67
+ end
68
+
69
+ task :ci do
70
+ sh %{bundle exec hippo db migrate}
71
+ sh %{bundle exec hippo db seed}
67
72
  sh %{bundle exec hippo jest}
68
73
  sh %{bundle exec rspec}
74
+ Rake::Task["lint"].invoke
69
75
  end
@@ -1,16 +1,22 @@
1
+ require 'hippo/webpack'
2
+ Hippo::Webpack.stub = true
3
+
1
4
  require 'rack/test'
2
5
  require 'hippo'
3
6
  require 'hippo/api'
4
7
  require 'hashie/mash'
5
8
  require 'active_record'
6
9
  require 'active_record/fixtures'
7
- require 'rack/test'
8
10
  require 'mail'
9
11
  require 'factory_girl'
10
12
  require 'faker'
11
13
  require 'database_cleaner'
12
14
  require "shrine/storage/memory"
13
15
  require 'vcr'
16
+ require 'hippo/tenant'
17
+
18
+
19
+ TEST_TENANT = Hippo::Tenant.find_by_slug('test') || Hippo::Tenant.create!(slug: 'test', name: 'testing tenant', email: 'test@test.com')
14
20
 
15
21
  VCR.configure do |config|
16
22
  config.cassette_library_dir = "spec/fixtures/vcr_cassettes"
@@ -30,13 +36,13 @@ VCR_OPTS = {
30
36
 
31
37
 
32
38
  module RackSpecMixin
33
- include Rack::Test::Methods
34
- def app
35
- Hippo::API::Root
36
- end
37
- def last_response_json
38
- Oj.load(last_response.body)
39
- end
39
+ include Rack::Test::Methods
40
+ def app
41
+ Hippo::API::Root
42
+ end
43
+ def last_response_json
44
+ Oj.load(last_response.body)
45
+ end
40
46
  end
41
47
 
42
48
  HIPPO_ENV = "test"
@@ -46,9 +52,23 @@ module Fixtures
46
52
  end
47
53
  end
48
54
 
55
+ FactoryGirl.definition_file_paths = Hippo::Extensions.map do |ext|
56
+ ext.root_path.join('spec/factories')
57
+ end
58
+
59
+ module ApiHelper
60
+ def ApiHelper.included(mod)
61
+ mod.around(:each) do |example|
62
+ begin
63
+ example.run
64
+ end
65
+ end
66
+ end
67
+ end
68
+
49
69
  RSpec.configure do |config|
50
70
  config.include FactoryGirl::Syntax::Methods
51
-
71
+ config.include ApiHelper, api: true
52
72
  config.include Fixtures
53
73
 
54
74
  config.include RackSpecMixin, :api => true
@@ -62,8 +82,10 @@ RSpec.configure do |config|
62
82
  end
63
83
 
64
84
  config.around(:each) do |example|
65
- DatabaseCleaner.cleaning do
66
- example.run
85
+ TEST_TENANT.perform do
86
+ DatabaseCleaner.cleaning do
87
+ example.run
88
+ end
67
89
  end
68
90
  end
69
91
 
@@ -1,6 +1,7 @@
1
1
  module Hippo
2
2
 
3
3
  class SystemSettings < Hippo::Model
4
+ belongs_to_tenant
4
5
 
5
6
  has_one :logo, as: :owner, class_name: 'Hippo::Asset',
6
7
  dependent: :destroy
@@ -14,7 +14,7 @@ module Hippo
14
14
  end
15
15
 
16
16
  def root_path
17
- Hippo::Extensions.for_identifier(extension_id.to_s).root_path.join('templates')
17
+ Hippo::ROOT_PATH.join('templates')
18
18
  end
19
19
 
20
20
  def pathname
@@ -0,0 +1,26 @@
1
+ module Hippo
2
+ module Templates
3
+ class Mail < Liquid
4
+
5
+ def to
6
+ raise 'virtual method to called, this should be implemented by child class'
7
+ end
8
+
9
+ def subject
10
+ raise 'virtual method subject called, this should be implemented by child class'
11
+ end
12
+
13
+ def pathname
14
+ root_path.join('mail', filename)
15
+ end
16
+
17
+ def variables
18
+ { 'product_name' => Hippo.config.product_name }
19
+ end
20
+
21
+ def self.create(*arg)
22
+ Hippo::Mailer.from_template(self.new(*arg))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module Hippo::Templates
2
+ class TenantChange < Mail
3
+
4
+ def initialize(tenant)
5
+ @tenant = tenant
6
+ end
7
+
8
+ def to
9
+ @tenant.email
10
+ end
11
+
12
+ def subject
13
+ "The login address for #{Hippo.config.product_name} has changed"
14
+ end
15
+
16
+ def variables
17
+ super.merge(
18
+ 'slug' => @tenant.slug,
19
+ 'domain' => @tenant.domain
20
+ )
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,53 @@
1
+ require 'activerecord-multi-tenant'
2
+
3
+ module Hippo
4
+ # Tenant
5
+ class Tenant < Hippo::Model
6
+ validates :slug, uniqueness: { case_sensitive: false }
7
+ validates :name, :presence => { message: 'for company' }
8
+ validates :email, :presence => true
9
+
10
+ has_random_identifier
11
+ has_many :users, class_name: 'Hippo::User', autosave: true
12
+
13
+ before_validation :auto_assign_slug, on: :create
14
+
15
+ def domain
16
+ self.slug + '.' + Hippo.config.website_domain
17
+ end
18
+
19
+ def perform
20
+ MultiTenant.with(self) do
21
+ yield
22
+ end
23
+ end
24
+
25
+ def self.current
26
+ MultiTenant.current_tenant
27
+ end
28
+
29
+ def self.system
30
+ find_by(slug: 'system') || create!(
31
+ slug: 'system', name: 'system',
32
+ email: 'contact@argosity.com', subscription: :admin
33
+ )
34
+ end
35
+
36
+ def self.switch(condition)
37
+ MultiTenant.current_tenant = self.find_by(condition)
38
+ end
39
+
40
+ protected
41
+
42
+ def auto_assign_slug
43
+ 5.times do |i|
44
+ if slug.blank?
45
+ newslug = Hippo::Strings.code_identifier(self.name, length: i + 5).downcase
46
+ self.slug = newslug if Tenant.where(slug: newslug).none?
47
+ end
48
+ break if slug.present?
49
+ end
50
+ end
51
+
52
+ end
53
+ end
@@ -3,10 +3,13 @@ require 'jwt'
3
3
  module Hippo
4
4
 
5
5
  class User < Hippo::Model
6
-
6
+ belongs_to_tenant
7
7
  has_secure_password
8
- validates :login, :name, :email, presence: true, uniqueness: { case_sensitive: false }
9
- validates :email, email: true, uniqueness: { case_sensitive: false }
8
+ validates :login, :name, presence: true,
9
+ uniqueness: { scope: :tenant, case_sensitive: false }
10
+ validates :email, presence: true, email: true,
11
+ uniqueness: { scope: :tenant, case_sensitive: false }
12
+
10
13
  validates :password, length: { minimum: 6 }, allow_nil: true
11
14
 
12
15
  whitelist_attributes :password
@@ -137,9 +140,12 @@ module Hippo
137
140
  end
138
141
 
139
142
  def self.seed_admin_account
140
- where(login: 'admin').first || create!(name: "Admin", email: "admin@test.com",
141
- password: 'password',
142
- login: 'admin', role_names: ['administrator'])
143
+ where(login: 'admin').first ||
144
+ create!(
145
+ name: "Admin", email: "admin@test.com",
146
+ password: 'password',
147
+ login: 'admin', role_names: ['administrator']
148
+ )
143
149
  end
144
150
  end
145
151
 
@@ -1,5 +1,5 @@
1
1
  module Hippo
2
2
 
3
- VERSION = "0.9.1"
3
+ VERSION = "0.9.2"
4
4
 
5
5
  end
@@ -0,0 +1,57 @@
1
+ require_relative './webpack/client_config'
2
+
3
+ module Hippo
4
+ class Webpack
5
+ mattr_accessor :stub
6
+ attr_reader :driver, :assets, :process
7
+
8
+ def initialize
9
+ @assets = {}
10
+ root = Hippo::Extensions.controlling.root_path
11
+
12
+ @config = ClientConfig.new
13
+ @config.invoke_all
14
+
15
+ @driver = WebpackDriver::Configuration.new(
16
+ directory: root,
17
+ logger: Hippo.logger,
18
+ file: root.join('config', 'webpack.config.js'),
19
+ cmd_line_flags: Hippo.env.production? ? [] : ['--hot', '--inline'],
20
+ environment: { NODE_ENV: Hippo.env.production? ? 'production' : 'development' }
21
+ )
22
+ @process = @driver.launch(development: !Hippo.env.production?)
23
+ end
24
+
25
+ def start
26
+ process.start if Hippo.env.development? && !Hippo::Webpack.stub
27
+ end
28
+
29
+ def wait_until_available
30
+ return if Hippo.env.production?
31
+ Hippo.logger.warn("waiting while compilation in progress") if busy?
32
+ sleep 0.5 while busy?
33
+ end
34
+
35
+ def busy?
36
+ driver.process.in_progress?
37
+ end
38
+
39
+ def file(entry, raise_on_not_found: true)
40
+ if Webpack.stub
41
+ "#{entry}.js"
42
+ else
43
+ asset = @driver.process.assets[entry]
44
+ if asset
45
+ asset.file
46
+ elsif !raise_on_not_found
47
+ raise ArgumentError, "asset '#{entry}' was not found"
48
+ end
49
+ end
50
+ end
51
+
52
+ def host
53
+ Hippo.env.production? ? "" :
54
+ "//#{@driver.process.host}:#{@driver.process.port}"
55
+ end
56
+ end
57
+ end
@@ -1,8 +1,8 @@
1
- require_relative './webpack_view';
1
+ require 'thor'
2
+ require 'active_support'
2
3
 
3
4
  module Hippo
4
- module Command
5
-
5
+ class Webpack
6
6
  class ClientConfig < Thor::Group
7
7
  include Thor::Actions
8
8
 
@@ -14,21 +14,17 @@ module Hippo
14
14
 
15
15
  attr_reader :module_paths
16
16
 
17
- ROOT = Pathname.new(__FILE__).dirname.join("..", "..", "..")
18
- def self.source_root
19
- ext = Hippo::Extensions.controlling
20
- ext.root_path
21
- end
17
+ ROOT = Pathname.new(__FILE__).dirname.join('..', '..', '..')
22
18
 
23
- def apply_hippo_config
24
- Command.load_current_extension
19
+ def self.source_root
25
20
  Hippo::Configuration.apply
26
21
  Hippo::Extensions.load_controlling_config
22
+ Hippo::Extensions.controlling.root_path
27
23
  end
28
24
 
29
25
  def set_vars
26
+ @controlling_extension = Hippo::Extensions.controlling
30
27
  @hippo_root_path = ROOT
31
- @controlling_extension = Command.load_current_extension(raise_on_fail: true)
32
28
  @extension_path = controlling_extension.root_path
33
29
  end
34
30
 
@@ -47,16 +43,6 @@ module Hippo
47
43
  def write_asset_files
48
44
  say "Generating config in #{directory}", :green
49
45
  opts = { verbose: false, force: true }
50
-
51
- Hippo::Extensions.controlling.view_templates.each do |tmpl|
52
- tmpl = WebpackView.new(tmpl)
53
- tmpl.write
54
- # set the mtime to the past, otherwise Webpack will build repeatedly as it starts up
55
- FileUtils.touch tmpl.destination.to_s, mtime: Time.now - 10.minute
56
- end
57
-
58
- template('config/webpack.config.js',
59
- directory.join('webpack.config.js'), opts)
60
46
  template('config/jest.config.json',
61
47
  directory.join('jest.config.json'), opts)
62
48
  template(ROOT.join('templates','js', 'config-data.js'),